当前位置: 首页 > news >正文

C-Stack_2025牛客暑期多校训练营6

words...

怎么大家都会,呜呜呜

题面

对于一个排列\(P\),定义\(f(P)\)如下:

def f(P:permutation) -> int:ans = 0st: Stack[int] = Stack()for i in P:while st is not empty and st.top() > i:st.pop()st.push(i)return st.size()

给定一个整数 \(n\),求所有长度为 \(n\)的排列\(P\)\(f(P)\)的立方和,结果对\(998244353\)取模。
形式化地:

\[\sum_{P \in S_n} f(P)^3 \mod 998244353 \]

\(S_n\)表示所有长度为 \(n\)的排列的集合。

  • 输入
    • 第一行包含一个正整数\(T\),表示测试用例的数量。
    • 接下来\(T\)行,每行包含一个正整数 \(n\),表示排列的长度。
  • 输出
    • 对于每个测试用例,输出一行一个整数,表示答案。

思路

转化

每次询问理应做到 \(O(1)\) ,需要预处理,可以把询问离线。

先分析对于某一个排列 \(P\) , \(f(P)\) 如何算:
需要一点点观察的是:

  • \(f(P)\)\([1,n]\) 之间
  • \(f(P)\) 等价于“排列 \(P\) 中从右往左极小值的个数”,这个个数记为 \(k\)

于是可以有如下转化

\[\sum_{P \in S_n} f(P)^3 = \sum_{k=1}^{n} k^3 \cdot s(n,k) \]

其中 \(s(n,k)\) 表示对于长度为 \(n\) 的排列,极小值个数为 \(k\) 的排列个数。

现在来看看怎么求解 \(s(n,k)\)
尝试能不能递推求解:

递推公式

考虑如何利用 \(n-1\) 个元素的排列来构造 \(n\) 个元素的排列,并分析新元素 \(n\) 对右侧极小值个数的影响。这里 \(n\) 是这 \(n\) 个数中最大的一个。

\(s(n,k)\) 表示长度为 \(n\) 的排列中右侧极小值个数为 \(k\) 的排列数。
我们有两种情况:

  1. 情况1:将元素 \(n\) 放在排列的最后一位。

    • 若在排列最后放入 \(n\),由于排列最后的元素必定为右侧极小值,所以新得到的排列中,\(n\) 会额外贡献一个右侧极小值。
    • 因此,原来 \(n-1\) 个元素的排列中的右侧极小值数必须为 \(k-1\)
    • 故有 \(s(n-1,k-1)\) 种方式。
  2. 情况2:将元素 \(n\) 插入到排列的末尾之外的任意位置。

    • 如果 \(n\) 不放在最后,显然 \(n\) 不会成为右侧极小值,因为它右边至少还有一个元素(且这些元素都比 \(n\) 小,因为 \(n\) 最大)。
    • 但是插入 \(n\) 会“破坏”原排列中某个右侧极小值的位置,使得右侧极小值的个数不增加。
    • 对于一个固定的 \(n-1\) 个元素的排列,保持右侧极小值数仍为 \(k\) 的排列有 \(s(n-1,k)\) 种,而插入 \(n\) 的位置有 \(n-1\) 个选择(排除最后一位)。
    • 故有 \((n-1) \cdot s(n-1,k)\) 种方式。

将两种情况相加,即可得到递推公式:

\[s(n,k) = s(n-1,k-1) + (n-1) \cdot s(n-1,k) \]

觉得眼熟?
这正是第一类无符号斯特林数的递推公式

处理边界条件

  • \(n = 0\) 时,我们定义 \(s(0,0)= 1\)(空排列记作 1 种方式)。
  • \(k = 0\)\(n \ge 1\) 时,无论如何排列至少有 1 个右侧极小值(最后的那个数),所以 \(s(n,0)=0\)
  • \(k = n\) 时,只有一种排列,即排列为严格递增的顺序,这时每个元素都是一个右侧极小值,所以 \(s(n,n)=1\)

第一类斯特林数的定义是:将 n 个两两不同的元素,划分为 k 个互不区分的非空轮换的方案数。


于是现在我们有:

  • \[ G(n)=\sum_{P \in S_n} f(P)^3 = \sum_{k=1}^{n} k^3 \cdot s(n,k) \]

  • \[n 为排列长度 \]

  • 知道了\(s(n,k)\) 是第一类斯特林数。

但是计算还是比较麻烦。

构造

接下来的操作可能会有点人类智慧,而且不是很显然。

思考要是式子能去掉求和就好了,这样比较容易算。

可以构造一个新函数

这里详细介绍如何想到利用生成函数来计算

\[S(n)=\sum_{k=1}^{n} c(n,k)\,k^3, \]

以及这一方法背后的动机和思路。


这里求的是带权和,于是可以想到生成函数

在组合计数问题中,当遇到这样的带权求和(即求 \((\sum_k (\text{计数数目}) \times w(k))\)),生成函数是一种非常自然和高效的工具,因为生成函数可以将序列的性质和依赖关系转化为函数形式,而系数的多项式求导与加权求和(如 \((\sum k \cdot a_k) 、(\sum k^2 \cdot a_k)\) 等)之间存在密切联系。

对于第一类无符号斯特林数,有一个经典的生成函数

\[F(t)= \sum_{k=0}^{n} c(n,k)\,t^k = t(t+1)(t+2)\cdots(t+n-1) = \]


我们对生成函数进行求导,可以得到关于 \(k\) 的加权和:

\[F(t)= \sum_{k=0}^{n} c(n,k)\,t^k \]

\[F'(t) = \sum_{k=0}^{n} k \cdot c(n,k) \, t^{k-1} \]

\[F''(t) = \sum_{k=0}^{n} k(k-1) \cdot c(n,k) \, t^{k-2} \]

\[F'''(t) = \sum_{k=0}^{n} k(k-1)(k-2) \cdot c(n,k) \, t^{k-3} \]

若令\(t=1\),则

\[F(1)=\sum_{k=0}^{n} c(n,k) = n!, \]

\[F'(1) = \sum_{k=0}^{n} k \cdot c(n,k) = \sum_{k=0}^{n} k \cdot c(n,k), \]

\[F''(1) = \sum_{k=0}^{n} k(k-1) \cdot c(n,k) = \sum_{k=0}^{n} k(k-1) \cdot c(n,k),\]

\[F'''(1) = \sum_{k=0}^{n} k(k-1)(k-2) \cdot c(n,k) = \sum_{k=0}^{n} k(k-1)(k-2) \cdot c(n,k). \]

求到三阶导就有\(k^3\) 了,考虑将\(F'''(1)\),\(F''(1)\),\(F'(1)\)结合起来,利用组合恒等式来分解 \(k^3\) 的求和。

解方程

\[k^3 = a \cdot(k(k-1)(k-2)) + b \cdot k(k-1) + c \cdot k,\]

对应项系数相等,解得:

\[a = 1, b = 3, c = 1. \]

因此,我们可以将 \(S(n)\) 表示为:

\[S(n) = F'''(1) + 3 \cdot F''(1) + F'(1). \]

OK,现在开始计算F(t)的表达式

已知的是:

\[F(t)=\sum_{k=0}^n s(n,k)t^k = t(t+1)(t+2)\cdots (t+n-1). \]

\[F(t)=\prod_{i=0}^{n-1} (t+i). \]

取自然对数,得到对数生成函数

\[\ln F(t)=\sum_{i=0}^{n-1}\ln(t+i). \]

\[G(t)=\ln F(t)=\sum_{i=0}^{n-1}\ln(t+i). \]

利用链式法则可以写出 \(F(t)\) 的导数与 \(G(t)\)的关系:

\[F'(t)=F(t) \,G'(t), \]

\[F''(t)=F(t)\Bigl[G''(t)+\bigl(G'(t)\bigr)^2\Bigr], \]

\[F'''(t)=F(t)\Bigl[G'''(t)+3G'(t)G''(t)+\bigl(G'(t)\bigr)^3\Bigr]. \]

接着计算 \(G(t)\) 及其在 \(t=1\) 处的各阶导数

\[G(1)=\sum_{i=0}^{n-1}\ln(1+i)=\sum_{j=1}^{n}\ln(j)=\ln(n!). \]

因此有

\[F(1)=e^{G(1)}=e^{\ln(n!)}=n!. \]

  1. 计算 \(G'(t)\)

    由于

    \[G(t)=\sum_{i=0}^{n-1}\ln(t+i), \]

    \(t\) 求导得

    \[G'(t)=\sum_{i=0}^{n-1}\frac{1}{t+i}. \]

    \(t=1\) 处,

    \[G'(1)=\sum_{i=0}^{n-1}\frac{1}{1+i}=\sum_{j=1}^{n}\frac{1}{j}=H(n). \]

    应用链式法则,

    \[F'(1)=F(1)\,G'(1)=n!\cdot H(n). \]

  2. 计算 \(G''(t)\)

    继续对 \(G'(t)\) 求导:

    \[G''(t)=-\sum_{i=0}^{n-1}\frac{1}{(t+i)^2}. \]

    \(t=1\),则

    \[G''(1)=-\sum_{i=0}^{n-1}\frac{1}{(1+i)^2}=-\sum_{j=1}^{n}\frac{1}{j^2}=-H_{2}(n). \]

    因此,

    \[F''(1)=F(1)\Bigl[G''(1)+\bigl(G'(1)\bigr)^2\Bigr] =n!\Bigl[-H_{2}(n)+H(n)^2\Bigr] =n!\cdot\Bigl(H(n)^2-H_{2}(n)\Bigr). \]

  3. 计算 \(G'''(t)\)

    \(G''(t)\) 再求导,有

    \[G'''(t)=\frac{d}{dt}\Bigl(-\sum_{i=0}^{n-1}\frac{1}{(t+i)^2}\Bigr) =2\sum_{i=0}^{n-1}\frac{1}{(t+i)^3}. \]

    \(t=1\) 时,

    \[G'''(1)=2\sum_{i=0}^{n-1}\frac{1}{(1+i)^3} =2\sum_{j=1}^{n}\frac{1}{j^3}=2H_{3}(n). \]

    再利用已知公式:

    \[F'''(1)=F(1)\Bigl[G'''(1)+3G'(1)G''(1)+\bigl(G'(1)\bigr)^3\Bigr], \]


  • \(F(1)=n!\)
  • \(F'(1)=n!\cdot H(n)\)
  • \(F''(1)=n!\cdot\Bigl(H(n)^2- H_{2}(n)\Bigr)\)
  • \(F'''(1)=n!\cdot\Bigl(H(n)^3-3H(n)H_{2}(n)+2H_{3}(n)\Bigr)\)

其中

\[H(n)=\sum_{j=1}^{n}\frac{1}{j},\quad H_{2}(n)=\sum_{j=1}^{n}\frac{1}{j^2},\quad H_{3}(n)=\sum_{j=1}^{n}\frac{1}{j^3}. \]


所以:

\[S(n) = F'''(1) + 3 \cdot F''(1) + F'(1) \]

\[= n!\cdot\Bigl(H(n)^3 + 3H(n)^2 - 3H(n)H_{2}(n) + 2H_{3}(n) - 3H_{2}(n) + H(n)\Bigr) \]

\(H(n)\),\(H_{2}(n)\),\(H_{3}(n)\) 都可以通过前缀和来计算。
还需要预处理阶乘
和逆元。

时间复杂度\(O(n \log n)\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;constexpr ll MOD = 998244353;
int main()
{ios::sync_with_stdio(0);cin.tie(0);int T;cin >> T;int mxn = 0;vector<int> ask(T);for (int i = 0; i < T; i++){cin >> ask[i];mxn = max(mxn, ask[i]);}vector<ll> fact(mxn + 1, 1);for (int i = 1; i <= mxn; i++)fact[i] = fact[i - 1] * i % MOD;vector<ll> inv(mxn + 1, 1);for (int i = 2; i <= mxn; i++)inv[i] = MOD - MOD / i * inv[MOD % i] % MOD;vector<ll> H(mxn + 1, 0),H2(mxn + 1, 0), H3(mxn + 1, 0);for (int i = 1; i <= mxn; i++){H[i] = (H[i - 1] + inv[i]) % MOD;H2[i] = (H2[i - 1] + inv[i] * inv[i] % MOD) % MOD;H3[i] = (H3[i - 1] + inv[i] * inv[i] % MOD * inv[i] % MOD) % MOD;}for (int i = 0; i < T; i++){int n = ask[i];ll h = H[n], h2 = H2[n], h3 = H3[n];ll res = (h * h % MOD * h % MOD + 3 * h * h % MOD % MOD + h) % MOD;res = (res - 3 * h * h2 % MOD + MOD) % MOD;res = (res - 3 * h2 % MOD + MOD) % MOD;res = (res + 2 * h3 % MOD) % MOD;ll ans = fact[n] * res % MOD;cout << ans << "\n";}return 0;
}
http://www.sczhlp.com/news/2532/

相关文章:

  • wqs 二分
  • 第3章,完善MBR
  • Ubuntu 安装 Docker 的方法(基于24.04 LTS)
  • 使用CONCAT函数将查询结果组合起来
  • 我的精神状态
  • 如何安装一只QT
  • Exceptionless docker部署 仪表盘不显示日志问题
  • Detectify如何助力企业实现NIS 2和DORA合规要求
  • P8990 [北大集训 2021] 小明的树
  • 在Docker中,镜像层级压缩如何实现?
  • 在Docker中,docker commit生成的镜像和dockerfile生成镜像有什么区别?
  • 如何优化Docker镜像大小?
  • 在Docker中,本地的镜像文件都存放在哪里?
  • 我给自己请了个“AI抬杠师”,吵了半小时,爽了
  • 在Docker中,Docker容器有几种状态?
  • 在Docker中,Docker可以用来做什么?
  • 在Docker中,stage和step有什么区别?
  • 在Docker中,如何查看镜像支持的环境变量?
  • 在Docker中,如何清理后台停止的容器?
  • Python:如何从地球大数据科学服务中心批量下载VPM-GPP?
  • 在Docker中,如何退出一个镜像的bash,而不终止它?
  • 在Docker中,Dockerfile有哪些常见指令?
  • 水土保持与荒漠化防治:考研之路在何方?
  • 7月31号
  • 2025杭电多校第三场 三带一、性质不同的数字、核心共振 个人题解 - CUC
  • 西北农林科技大学专业排名大揭秘
  • 2025杭电多校第四场 回忆与展望、量子弹弓 个人题解 - CUC
  • 高效绩效校准:管理者视角下的绩效场景优化
  • Python列表完全指南:从基础到实战(2025版)
  • 全球首个搭载 Kimi-K2 的 Serverless 架构 VibeCoding解决方案重磅来袭!