2021ICPC济南区域赛题解CDJKL

2021ICPC济南区域赛题解

题目链接:
https://pintia.cn/problem-sets/1459829212832296960

讲义、代码链接:
https://hytidel.lanzoub.com/b031c9dti
密码:cj1j

视频讲解链接:
https://www.bilibili.com/video/BV1Qe4y1D7c4



K. Search For Mafuyu

题意

有一棵包含编号为 1 ∼ n 1\sim n 1n n n n个节点的树.初始时你在 1 1 1号节点,你想找的人M等概率地在 2 ∼ n 2\sim n 2n号节点.每一秒,你可走到与当前节点相邻的节点处.若你采取最优策略,求你找到M所需的时间的期望.

t    ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t  (1t1000)组测试数据.每组测试数据输入一个整数 n    ( 2 ≤ n ≤ 100 ) n\ \ (2\leq n\leq 100) n  (2n100),表示节点数.接下来 ( n − 1 ) (n-1) (n1)行每行输入两个数 a , b    ( 1 ≤ a , b ≤ n ) a,b\ \ (1\leq a,b\leq n) a,b  (1a,bn),表示节点 a a a b b b间有边.数据保证输入的信息构成一棵树.

对每组测试数据,输出你采取最优策略时找到M所需的时间的期望,误差不超过 1 e − 9 1\mathrm{e}-9 1e9.

思路

最优策略是走树的Euler序,即不走到底不回头.

[] 若不然,设(遍历时,即深度增加时)你从节点 u u u走到节点 v v v(非叶子节点)后又走回节点 u u u.

(1)走到节点 u u u时游戏未结束,说明M不在节点 u u u处.走到节点 v v v时游戏未结束,说明M不在节点 v v v处.

​ 此时若从 v v v返回 u u u,则需多花 1   s 1\ \mathrm{s} 1 s检查一个已经确认没有M的节点,显然这不是最优的.

(2)若M在节点 v v v的子树中,显然继续遍历 v v v的子树比先回到 u u u再遍历 u u u的子树更优.

(3)若M不在节点 v v v的子树中(这需遍历完 v v v的子树才能确定),又M不在已遍历过的节点中,则需从 v v v回溯到 u u u,继续遍历其他子树.

​ 显然这种遍历顺序是Euler序.

[] 由(2)(3)知:交换两子树的遍历顺序,所需的时间的期望不变.

对M在 i    ( 2 ≤ i ≤ n ) i\ \ (2\leq i\leq n) i  (2in)节点的情况,只需在DFS过程中更新节点出入栈的时间即可得到Euler序,将Euler序累加到答案上,最后除以 ( n − 1 ) (n-1) (n1)即可.

代码 -> 2021ICPC济南-K(Euler序+期望)

const int MAXN = 105;
int n;  // 节点数
vi edges[MAXN];
double ans = 0;
int tim;  // 时间

void dfs(int u, int fa) {
	tim++;  // u入栈
	for (auto v : edges[u]) {
		if (v == fa) continue;

		ans += tim;  // 将Euler序累加到答案上
		dfs(v, u);
	}
	tim++;  // u出栈
}

int main() {
	CaseT{
		cin >> n;
		ans = tim = 0;  // 清空
		for (int i = 1; i <= n; i++) edges[i].clear();

		for (int i = 0; i < n - 1; i++) {
			int u, v; cin >> u >> v;
			edges[u].push_back(v), edges[v].push_back(u);
		}

		dfs(1, -1);  // 从起点开始搜,起点无前驱节点
		cout << fixed << setprecision(18) << ans / (n - 1) << endl;
	}
}


C. Optimal Strategy

题意

有编号 1 ∼ n 1\sim n 1n n    ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n  (1n1e6)个物品,其中第 i    ( 1 ≤ i ≤ n ) i\ \ (1\leq i\leq n) i  (1in)个物品的价值为 a i    ( 1 ≤ a i ≤ n ) a_i\ \ (1\leq a_i\leq n) ai  (1ain).A和B两人轮流操作,A先手.当前轮到的人在剩余的物品中取走一个物品.要求当所有物品取完时,两人各自手中的物品的价值之和都最大.两人都采取最优策略,求有多少种游戏进行序列(每轮玩家取的物品的编号构成的序列),答案对 998244353 998244353 998244353取模.

思路

常见误区:最优策略是每次都拿剩下的物品中价值最大的物品.对样例 1 1 1,若应拿价值最大的物品,则先手不应拿 1 1 1,与样例解释不符.事实上,要使得游戏结束时两人各自手中的物品价值之和都最大,只需保证两人都拿到取得自己物品价值之和的最大值应拿的那些物品即可,与什么时候拿到那个物品无关.

显然游戏的结果只有先手必胜和平局两种情况,但这题不是博弈论,而是纯纯的组合计数.

具体地考察最优策略中两人分别应拿到哪些数.设 c n t [ i ] cnt[i] cnt[i]表示价值为 i i i的数的个数.① c n t [ i ] cnt[i] cnt[i]为偶数时,显然A和B各拿一半;② c n t [ i ] cnt[i] cnt[i]为奇数时,A先手会多拿走一个,转化为B先手且 c n t [ i ] cnt[i] cnt[i]为偶数的情况.综上,对 c n t [ i ] cnt[i] cnt[i]个数 i i i,只需考虑其中 ⌊ c n t [ i ] 2 ⌋ \left\lfloor\dfrac{cnt[i]}{2}\right\rfloor 2cnt[i]个的分配情况.

注意到 1 ≤ a i ≤ 1 e 6 1\leq a_i\leq1\mathrm{e}6 1ai1e6,不妨按顺序枚举每个数值的分配情况,而两人何时拿到自己应拿的物品不影响最终两人各自手中的物品的价值之和,故从小到大或从大到小枚举每个数值的分配情况都可以,下面以从小到大枚举为例.设当前准备分配数 i i i,则前面的 1 ∼ ( i − 1 ) 1\sim (i-1) 1(i1) s u m = ∑ j = 1 i − 1 c n t [ j ] \displaystyle sum=\sum_{j=1}^{i-1}cnt[j] sum=j=1i1cnt[j]个数都已分配好,只需在前 ( s u m + ⌊ c n t [ i ] 2 ⌋ ) \left(sum+\left\lfloor\dfrac{cnt[i]}{2}\right\rfloor\right) (sum+2cnt[i])个数中选 ⌊ c n t [ i ] 2 ⌋ \left\lfloor\dfrac{cnt[i]}{2}\right\rfloor 2cnt[i]个给先手即可,方案数为 C s u m + ⌊ c n t [ i ] / 2 ⌋ ⌊ c n t [ i ] / 2 ⌋ C_{sum+\left\lfloor cnt[i]/2\right\rfloor}^{\left\lfloor cnt[i]/2\right\rfloor} Csum+cnt[i]/2cnt[i]/2.因以不同顺序拿到相同数值的数视为不同方案,故 a n s = ∑ i = 1 n c n t [ i ] ! ⋅ C s u m + ⌊ c n t [ i ] / 2 ⌋ ⌊ c n t [ i ] / 2 ⌋ \displaystyle ans=\sum_{i=1}^n cnt[i]!\cdot C_{sum+\left\lfloor cnt[i]/2\right\rfloor}^{\left\lfloor cnt[i]/2\right\rfloor} ans=i=1ncnt[i]!Csum+cnt[i]/2cnt[i]/2.直接计算时间复杂度为 O ( n 2 ) O(n^2) O(n2),注意到 s u m sum sum c n t [ j ] cnt[j] cnt[j]的前缀和,只需在转移的同时维护即可,时间复杂度 O ( n ) O(n) O(n).

代码 -> 2021ICPC济南-C(组合计数+推公式+前缀和)

const int MAXN = 1e6 + 5;
const int MOD = 998244353;
int n;
int cnt[MAXN];  // cnt[i]表示值为i的数的个数
int fac[MAXN], ifac[MAXN];

void init() {  // 预处理阶乘及其逆元
	fac[0] = 1;
	for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;

	ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
	for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;
}

int C(int n, int m) {  // 组合数C(n,m)
	return (ll)fac[n] * ifac[m] % MOD * ifac[n - m] % MOD;
}

int main() {
	init();

	cin >> n;
	for (int i = 0; i < n; i++) {
		int a; cin >> a;
		cnt[a]++;
	}

	int ans = 1;  // 注意初始值为1而不是0
	int pre = 0;  // cnt[i]的前缀和
	for (int i = 1; i <= n; i++) {
		if (!cnt[i]) continue;

		ans = (ll)ans * fac[cnt[i]] % MOD * C(pre + cnt[i] / 2, cnt[i] / 2) % MOD;
		pre += cnt[i];
	}
	cout << ans;
}


D. Arithmetic Sequence

题意

给定一个长度为 n    ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n  (1n2e5)的数列 a 1 , ⋯   , a n    ( 0 ≤ ∣ a i ∣ ≤ 1 e 13 ) a_1,\cdots,a_n\ \ (0\leq |a_i|\leq 1\mathrm{e}13) a1,,an  (0ai1e13).现有两种操作:①选择数列中的一个数 + 1 +1 +1;②选择数列中的一个数 − 1 -1 1.求将该数列变为等差数列的最小操作次数.

思路 by : keguaiguai

设最优解得到的数列为 b 1 , ⋯   , b n b_1,\cdots,b_n b1,,bn,其公差为 d d d,则 b i = b 1 + ( i − 1 ) d b_i=b_1+(i-1)d bi=b1+(i1)d,所需的操作次数为 ∑ i = 1 n ∣ a i − b i ∣ = ∑ i = 1 n ∣ [ a i − ( i − 1 ) d ] − b 1 ∣ = c i = a i − ( i − 1 ) d ∑ i = 1 n ∣ c i − b 1 ∣ \displaystyle\sum_{i=1}^n |a_i-b_i|=\sum_{i=1}^n \left|[a_i-(i-1)d]-b_1\right|\xlongequal{c_i=a_i-(i-1)d}\sum_{i=1}^n |c_i-b_1| i=1naibi=i=1n[ai(i1)d]b1ci=ai(i1)d i=1ncib1,易想到货舱选址问题.注意到此时未知 c i c_i ci的值,而 c i c_i ci的值可由 a i a_i ai d d d确定,考虑先求出 d d d.

设将序列变为以 d d d为公差的等差数列所需的最小操作次数为 f ( d ) f(d) f(d),它是一个下凸函数.

[] 注意到 ∣ [ a i − ( i − 1 ) ( d + 1 ) ] − b 1 ∣ + ∣ [ a i − ( i − 1 ) ( d − 1 ) ] − b 1 ∣ \left|[a_i-(i-1)(d+1)]-b_1\right|+\left|[a_i-(i-1)(d-1)]-b_1\right| [ai(i1)(d+1)]b1+[ai(i1)(d1)]b1

= ∣ [ a i − ( i − 1 ) d − b 1 ] − ( i − 1 ) ∣ + ∣ [ a i − ( i − 1 ) d − b 1 ] + ( i − 1 ) ∣ ≥ 2 ∣ [ a i − ( i − 1 ) d ] − b 1 ∣ =|[a_i-(i-1)d-b_1]-(i-1)|+|[a_i-(i-1)d-b_1]+(i-1)|\geq 2|[a_i-(i-1)d]-b_1| =[ai(i1)db1](i1)+[ai(i1)db1]+(i1)2∣[ai(i1)d]b1,

两边求和得 f ( d + 1 ) + f ( d − 1 ) ≥ 2 f ( d ) f(d+1)+f(d-1)\geq 2f(d) f(d+1)+f(d1)2f(d),故证.

注意到下凸函数是单谷的,可三分出其最小值点.注意每次三分都要求出序列的中位数,用sort可能会TLE,故改用nth_element函数在 O ( n ) O(n) O(n)的时间复杂度内求出序列的中位数.

注意到 n n n最大 2 e 5 , a i 2\mathrm{e}5,a_i 2e5,ai最大 1 e 13 1\mathrm{e}13 1e13,则累加后可能会爆ll,故要开__int128.注意对__int128要重载abs函数.

代码 -> 2021ICPC济南-D(三分+nth_element)

#define ll __int128

const int MAXN = 2e5 + 5;
int n;
ll a[MAXN];  // 原数列
ll b[MAXN];  // 目标数列

ll abs(ll x) { return x < 0 ? -x : x; }  // 对__int128重载abs()

ll get_ans(ll d) {
	for (int i = 1; i <= n; i++) b[i] = a[i] - (i - 1) * d;

	ll res = 0;
	nth_element(b + 1, b + (n + 1) / 2, b + n + 1);  // 将中位数调整到下标(n+1)/2处
	ll mid = b[(n + 1) / 2];  // 中位数

	for (int i = 1; i <= n; i++) res += abs(a[i] - mid - (i - 1) * d);
	return res;
}

int main() {
	read(n);
	for (int i = 1; i <= n; i++) read(a[i]);

	ll l = -2e13, r = 2e13;
	while (l < r) {
		ll lmid = l + (r - l) / 3, rmid = r - (r - l) / 3;
		if (get_ans(lmid) < get_ans(rmid)) r = rmid - 1;
		else l = lmid + 1;
	}
	write(get_ans(l));
}


J. Determinant

题意

n n n阶矩阵 A A A有性质: det ⁡ ( A T A ) = ( det ⁡ A ) 2 \det(A^TA)=(\det A)^2 det(ATA)=(detA)2.给定 n n n阶矩阵 A A A ∣ det ⁡ A ∣ |\det A| detA,判断 det ⁡ A \det A detA的正负.

t    ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t  (1t100)组测试数据.每组测试数据第一行输入整数 n    ( 1 ≤ n ≤ 100 ) n\ \ (1\leq n\leq 100) n  (1n100).第二行输入一个长度不超过 1 e 4 1\mathrm{e}4 1e4的大数表示 ∣ det ⁡ A ∣ |\det A| detA.接下来输入 n × n n\times n n×n的矩阵 A A A,其中元素的绝对值不超过 1 e 9 1\mathrm{e}9 1e9.

对每组测试数据,若 det ⁡ A > 0 \det A>0 detA>0,输出’+‘;否则输出’-'.

思路 by : Paranoid5

[引理] 对$\forall 非零整数 非零整数 非零整数x_1 及其相反数 及其相反数 及其相反数x_2 , 对奇模数 ,对奇模数 ,对奇模数p , 有 ,有 ,x_1\not\equiv x_2\ \ (\mathrm{mod}\ p) , 且 ,且 ,x_1\ \mathrm{mod}\ p\neq 0,x_2\ \mathrm{mod}\ p\neq 0$.

[] x 1 + x 2 = 0 x_1+x_2=0 x1+x2=0,则 ( x 1 + x 2 )   m o d   p = 0 (x_1+x_2)\ \mathrm{mod}\ p=0 (x1+x2) mod p=0,进而 ( x 1   m o d   p + x 2   m o d   p )   m o d   p = 0 (x_1\ \mathrm{mod}\ p+x_2\ \mathrm{mod}\ p)\ \mathrm{mod}\ p=0 (x1 mod p+x2 mod p) mod p=0.

t i = x i   m o d   p    ( i = 1 , 2 ) t_i=x_i\ \mathrm{mod}\ p\ \ (i=1,2) ti=xi mod p  (i=1,2).若 x 1 ≡ x 2    ( m o d   p ) x_1\equiv x_2\ \ (\mathrm{mod}\ p) x1x2  (mod p),则 t 1 = t 2 t_1=t_2 t1=t2,且 t 1 , t 2 < p t_1,t_2

t1,t2<p.

​ 此时 t 1 + t 2 t_1+t_2 t1+t2为偶数,且 0 < t 1 + t 2 < 2 p 00<t1+t2<2p.

( t 1 + t 2 )   m o d   p = 0 (t_1+t_2)\ \mathrm{mod}\ p=0 (t1+t2) mod p=0,则 t 1 + t 2 = k p    ( k ∈ Z ) t_1+t_2=kp\ \ (k\in\mathbb{Z}) t1+t2=kp  (kZ),结合 0 < t 1 + t 2 < 2 p 00<t1+t2<2p k ∈ ( 0 , 2 ) k\in(0,2) k(0,2),即 k = 1 k=1 k=1.

​ 此时 t 1 + t 2 = p t_1+t_2=p t1+t2=p, L H S \mathrm{LHS} LHS为偶数, R H S \mathrm{RHS} RHS为奇数,矛盾.

x 1 ≢ x 2    ( m o d   p ) x_1\not\equiv x_2\ \ (\mathrm{mod}\ p) x1x2  (mod p).

∣ det ⁡ A ∣ |\det A| detA对大素数 M O D MOD MOD取模,用Gauss消元求 det ⁡ A \det A detA在模 M O D MOD MOD意义下的值,比较两者是否相等即可.常见的模数可取 1 e 9 + 7 1\mathrm{e}9+7 1e9+7.

代码 -> 2021ICPC济南-J(Gauss消元+大数取模)

const int MAXN = 605;
const int MOD = 1e9 + 7;
int n;
int a[MAXN][MAXN];

int gauss() {
	int sgn = 1;  // 行列式的值的符号
	for (int i = 1; i <= n; i++) {
		for (int j = i + 1; j <= n; j++) {
			while (a[i][i]) {
				int d = a[j][i] / a[i][i];
				for (int k = i; k <= n; k++)
					a[j][k] = ((ll)a[j][k] - (ll)d * a[i][k] % MOD + MOD) % MOD;
				swap(a[i], a[j]);
				sgn *= -1;
			}
			swap(a[i], a[j]);
			sgn *= -1;
		}
	}
	
	int res = 1;
	for (int i = 1; i <= n; i++) res = (ll)res * a[i][i] % MOD;
	return (res * sgn + MOD) % MOD;
}

int main() {
	CaseT{
		cin >> n;
		string s; cin >> s;
		int ans = 0;
		for (auto ch : s) ans = ((ll)ans * 10 % MOD + ch - '0') % MOD;

		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++) cin >> a[i][j];

		if (ans == gauss()) cout << '+' << endl;
		else cout << '-' << endl;
	}
}


L. Strange Series

题意 ( 2   s 2\ \mathrm{s} 2 s)

给定一个 n n n次多项式 P ( x ) = a 0 + a 1 x + ⋯ + a n x n P(x)=a_0+a_1x+\cdots+a_nx^n P(x)=a0+a1x++anxn,其中 a i ≥ 0    ( i = 0 , ⋯   , n ) a_i\geq 0\ \ (i=0,\cdots,n) ai0  (i=0,,n).考察级数 S = ∑ m = 0 ∞ P ( m ) m ! \displaystyle S=\sum_{m=0}^\infty \dfrac{P(m)}{m!} S=m=0m!P(m).可以证明 S S S是良好定义的,且 S = p e S=p\mathrm{e} S=pe,其中 p p p是一个非负整数, e = ∑ m = 0 ∞ 1 m ! ≈ 2.71828 \displaystyle \mathrm{e}=\sum_{m=0}^\infty \dfrac{1}{m!}\approx 2.71828 e=m=0m!12.71828.求 p p p.

t    ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t  (1t100)组测试数据.每组测试数据输入一个整数 n    ( 0 ≤ n ≤ 1 e 5 ) n\ \ (0\leq n\leq 1\mathrm{e}5) n  (0n1e5).第二行输入 ( n + 1 ) (n+1) (n+1)个整数 a 0 , a 1 , ⋯   , a n    ( 0 ≤ a i < 998244353 ) a_0,a_1,\cdots,a_n\ \ (0\leq a_i<998244353) a0,a1,,an  (0ai<998244353).数据保证至多有 4 4 4组测试数据满足 n > 1000 n>1000 n>1000.

对每组测试数据,输出 p p p 998244353 998244353 998244353取模的结果.

思路

[引理] ∑ m = 0 ∞ m n m ! x m = e x ⋅ Q ( x ) \displaystyle\sum_{m=0}^\infty \dfrac{m^n}{m!}x^m=\mathrm{e}^x\cdot Q(x) m=0m!mnxm=exQ(x),其中 Q ( x ) Q(x) Q(x)是关于 x x x n n n次多项式.

[] 用数学归纳法证明.不妨设 n − 1 n-1 n1时结论成立.

∑ m = 0 ∞ 1 m ! ⋅ m n x m = x ∑ m = 0 ∞ 1 m ! ⋅ m n − 1 ⋅ m x m − 1 = x ∑ m = 0 ∞ 1 m ! ⋅ m n − 1 ⋅ d d x ( x m ) = x ⋅ d d x ∑ m = 0 ∞ 1 m ! ⋅ m n − 1 x m \displaystyle \sum_{m=0}^\infty \dfrac{1}{m!}\cdot m^nx^m=x\sum_{m=0}^\infty \dfrac{1}{m!}\cdot m^{n-1}\cdot mx^{m-1}=x\sum_{m=0}^\infty \dfrac{1}{m!}\cdot m^{n-1}\cdot\dfrac{\mathrm{d}}{\mathrm{d}x}(x^m)=x\cdot \dfrac{\mathrm{d}}{\mathrm{d}x}\sum_{m=0}^\infty \dfrac{1}{m!}\cdot m^{n-1} x^m m=0m!1mnxm=xm=0m!1mn1mxm1=xm=0m!1mn1dxd(xm)=xdxdm=0m!1mn1xm

= x ⋅ d d x [ e x Q ( x ) ] =x\cdot\dfrac{\mathrm{d}}{\mathrm{d}x}[\mathrm{e}^x Q(x)] =xdxd[exQ(x)],其中 Q ( x ) Q(x) Q(x)是关于 x x x ( n − 1 ) (n-1) (n1)次多项式.

注意到对 e x Q ( x ) \mathrm{e}^xQ(x) exQ(x)求导后多项式部分的最高次项仍为 x n − 1 x^{n-1} xn1,乘上前面的 x x x即证.

P ( x ) = a 0 + a 1 x + ⋯ + a n x n    ( a i ≥ 0 ) , S = ∑ m = 0 ∞ P ( m ) m ! = p e P(x)=a_0+a_1x+\cdots+a_nx^n\ \ (a_i\geq 0),\displaystyle S=\sum_{m=0}^\infty \dfrac{P(m)}{m!}=p\mathrm e P(x)=a0+a1x++anxn  (ai0),S=m=0m!P(m)=pe,则 p = 1 e S = 1 e lim ⁡ s → ∞ ∑ m = 0 s P ( m ) m ! \displaystyle p=\dfrac{1}{\mathrm{e}}S=\dfrac{1}{\mathrm{e}}\lim_{s\rightarrow\infty}\sum_{m=0}^s \dfrac{P(m)}{m!} p=e1S=e1slimm=0sm!P(m).

P ( m ) m ! = 1 m ! ( a 0 + a 1 m + ⋯ + a n m n ) = ∑ i = 0 n a i k i k ! \dfrac{P(m)}{m!}=\dfrac{1}{m!}(a_0+a_1m+\cdots+a_nm^n)=\displaystyle\sum_{i=0}^n a_i\dfrac{k^i}{k!} m!P(m)=m!1(a0+a1m++anmn)=i=0naik!ki,

​ 则 p = 1 e lim ⁡ s → ∞ ∑ m = 0 s ∑ i = 0 n a i m i m ! = ∑ i = 0 n a i ( 1 e lim ⁡ s → ∞ ∑ m = 0 s m i m ! ) = ∑ i = 0 n a i [ 1 e ∑ m = 0 ∞ m i m ! ] \displaystyle p=\dfrac{1}{\mathrm{e}}\lim_{s\rightarrow\infty}\sum_{m=0}^s \sum_{i=0}^n a_i\dfrac{m^i}{m!}=\sum_{i=0}^n a_i\left(\dfrac{1}{\mathrm{e}}\lim_{s\rightarrow\infty}\sum_{m=0}^s \dfrac{m^i}{m!}\right)=\sum_{i=0}^n a_i\left[\dfrac{1}{\mathrm{e}}\sum_{m=0}^\infty \dfrac{m^i}{m!}\right] p=e1slimm=0si=0naim!mi=i=0nai(e1slimm=0sm!mi)=i=0nai[e1m=0m!mi].

由引理知: ∑ m = 0 ∞ m i m ! x m = e x ⋅ Q ( x ) \displaystyle \sum_{m=0}^\infty \dfrac{m^i}{m!}x^m=\mathrm{e}^x\cdot Q(x) m=0m!mixm=exQ(x),其中 Q ( x ) Q(x) Q(x)是关于 x x x i i i次多项式,进而 p = ∑ i = 0 n a i ⋅ Q ( 1 ) \displaystyle p=\sum_{i=0}^n a_i \cdot Q(1) p=i=0naiQ(1),同时也证明了 p ∈ N p\in\mathbb{N} pN.

下面讨论如何求 Q ( x ) Q(x) Q(x)的系数.

i = 0 i=0 i=0时, ∑ m = 0 ∞ 1 m ! x m = e x ⋅ 1 \displaystyle \sum_{m=0}^\infty \dfrac{1}{m!}x^m=\mathrm{e}^x\cdot 1 m=0m!1xm=ex1,此时 Q ( x ) = 1 , Q ( 1 ) = 1 Q(x)=1,Q(1)=1 Q(x)=1,Q(1)=1.

i = 1 i=1 i=1时, ∑ m = 1 ∞ m m ! x m = e x ⋅ x \displaystyle \sum_{m=1}^\infty \dfrac{m}{m!}x^m=\mathrm{e}^x\cdot x m=1m!mxm=exx,此时 Q ( x ) = x , Q ( 1 ) = 1 Q(x)=x,Q(1)=1 Q(x)=x,Q(1)=1.

i = 2 i=2 i=2时, ∑ m = 1 ∞ m 2 m ! x m = e x ⋅ x ( x + 1 ) \displaystyle \sum_{m=1}^\infty \dfrac{m^2}{m!}x^m=\mathrm{e}^x\cdot x(x+1) m=1m!m2xm=exx(x+1),此时 Q ( x ) = x ( x + 1 ) , Q ( 1 ) = 2 Q(x)=x(x+1),Q(1)=2 Q(x)=x(x+1),Q(1)=2.

i = 3 i=3 i=3时, ∑ m = 1 ∞ m 3 m ! x m = e x ⋅ x ( x 2 + 3 x + 1 ) \displaystyle \sum_{m=1}^\infty \dfrac{m^3}{m!}x^m=\mathrm{e}^x\cdot x(x^2+3x+1) m=1m!m3xm=exx(x2+3x+1),此时 Q ( x ) = x ( x 2 + 3 x + 1 ) , Q ( 1 ) = 5 Q(x)=x(x^2+3x+1),Q(1)=5 Q(x)=x(x2+3x+1),Q(1)=5.

i = 4 i=4 i=4时, ∑ m = 1 ∞ m 4 m ! x m = e x ⋅ x ( x 3 + 6 x 2 + 7 x + 1 ) \displaystyle \sum_{m=1}^\infty \dfrac{m^4}{m!}x^m=\mathrm{e}^x\cdot x(x^3+6x^2+7x+1) m=1m!m4xm=exx(x3+6x2+7x+1),此时 Q ( x ) = x ( x 3 + 6 x 2 + 7 x + 1 ) , Q ( 1 ) = 15 Q(x)=x(x^3+6x^2+7x+1),Q(1)=15 Q(x)=x(x3+6x2+7x+1),Q(1)=15.

i = 5 i=5 i=5时, ∑ m = 1 ∞ m 5 m ! x m = e x ⋅ x ( x 4 + 10 x 3 + 25 x 2 + 15 x + 1 ) \displaystyle \sum_{m=1}^\infty \dfrac{m^5}{m!}x^m=\mathrm{e}^x\cdot x(x^4+10x^3+25x^2+15x+1) m=1m!m5xm=exx(x4+10x3+25x2+15x+1),

​ 此时 Q ( x ) = x ( x 4 + 10 x 3 + 25 x 2 + 15 x + 1 ) , Q ( 1 ) = 52 Q(x)=x(x^4+10x^3+25x^2+15x+1),Q(1)=52 Q(x)=x(x4+10x3+25x2+15x+1),Q(1)=52.

易联想到Bell数: B 0 = 1 , B 1 = 1 , B 2 = 2 , B 3 = 5 , B 4 = 15 , B 5 = 52 , ⋯ B_0=1,B_1=1,B_2=2,B_3=5,B_4=15,B_5=52,\cdots B0=1,B1=1,B2=2,B3=5,B4=15,B5=52,,它可用递推求得.

注意到 n n n最大为 1 e 5 1\mathrm{e}5 1e5,暴力递推出Bell数会TLE.考虑用Bell数的生成函数求Bell数.

Bell数的定义:将 n n n个不同的元素的集合划分为任意多个非空子集的方案数.

n n n个元素的方案数为 f n f_n fn.固定最后一个元素 x x x,枚举最后一个元素所在的集合的元素个数 i ∈ [ 1 , n ] i\in[1,n] i[1,n],从剩下的 ( n − 1 ) (n-1) (n1)个元素中选 ( i − 1 ) (i-1) (i1)个元素与 x x x在同一集合中,其他 ( n − i ) (n-i) (ni)个元素任意划分,故 f n = ∑ i = 1 n C n − 1 i − 1 ⋅ f n − i \displaystyle f_n=\sum_{i=1}^n C_{n-1}^{i-1}\cdot f_{n-i} fn=i=1nCn1i1fni,调整下标得 f n + 1 = ∑ i = 0 n C n i ⋅ f n − i \displaystyle f_{n+1}=\sum_{i=0}^n C_n^i\cdot f_{n-i} fn+1=i=0nCnifni.

f f f的EGF为 F ( x ) F(x) F(x),则 f n + 1 ( n + 1 ) ! = ∑ i = 0 n f n − i ( n − i ) ! ⋅ 1 i ! ( n + 1 ) \displaystyle \dfrac{f_{n+1}}{(n+1)!}=\sum_{i=0}^n \dfrac{f_{n-i}}{(n-i)!}\cdot\dfrac{1}{i!(n+1)} (n+1)!fn+1=i=0n(ni)!fnii!(n+1)1,进而 ( n + 1 ) ⋅ f n + 1 ( n + 1 ) ! = ∑ i = 0 n f n − i ( n − i ) ! ⋅ 1 i ! \displaystyle (n+1)\cdot\dfrac{f_{n+1}}{(n+1)!}=\sum_{i=0}^n \dfrac{f_{n-i}}{(n-i)!}\cdot\dfrac{1}{i!} (n+1)(n+1)!fn+1=i=0n(ni)!fnii!1,

​ 即 [ x n ] F ’ ( x ) = ∑ i = 0 n [ x n − i ] F ( x ) ⋅ [ x i ] e x \displaystyle [x^n]F’(x)=\sum_{i=0}^n [x^{n-i}]F(x)\cdot [x^i]\mathrm{e}^x [xn]F(x)=i=0n[xni]F(x)[xi]ex,亦即 F ′ ( x ) = F ( x ) e x F'(x)=F(x)\mathrm{e}^x F(x)=F(x)ex.

y = F ( x ) y=F(x) y=F(x),则 d y d x = e x y \dfrac{\mathrm{d}y}{\mathrm{d}x}=\mathrm{e}^xy dxdy=exy,移项得 d y y = e x d x \dfrac{\mathrm{d}y}{y}=\mathrm{e}^x\mathrm{d}x ydy=exdx,两边积分得 ln ⁡ y = e x + C \ln y=\mathrm{e}^x+C lny=ex+C,故 y = exp ⁡ ( e x + C ) y=\exp(\mathrm{e}^x+C) y=exp(ex+C).

n = 1 n=1 n=1时只有一种划分方案,代入得 C = − 1 C=-1 C=1,故 F ( x ) = exp ⁡ ( e x − 1 ) F(x)=\exp(\mathrm{e}^x-1) F(x)=exp(ex1).多项式exp求即可,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码 -> 2021ICPC济南-L(Bell数+生成函数)

const int MAXN = 6e5 + 5;
const int MOD = 998244353;

int rev[MAXN];  // rev[x]表示二进制数x镜像后的结果

void NTT(int* y, int lim, int op) {  // op=1时为NTT,op=-1时为INTT
	for (int i = 0; i < lim; i++)  // 做一遍BRP,将系数位置调整为递归后的位置
		if (rev[i] < i) swap(y[i], y[rev[i]]);

	for (int l = 2; l <= lim; l <<= 1) {  // 模拟合并过程,l为当前区间长度
		int g = qpow(3, (MOD - 1) / l, MOD);
		for (int i = 0; i < lim; i += l) {
			int cur = 1;
			int k = l >> 1;
			for (int j = 0; j < k; j++, cur = (ll)cur * g % MOD) {
				int tmp = (ll)y[i + j + k] * cur % MOD;
				y[i + j + k] = (y[i + j] - tmp + MOD) % MOD;  // 奇次项
				y[i + j] = ((ll)y[i + j] + tmp) % MOD;  // 偶次项
			}
		}
	}

	if (op == -1) {  // INTT
		reverse(y + 1, y + lim);
		int inv = qpow(lim, MOD - 2, MOD);
		for (int i = 0; i < lim; i++) y[i] = (ll)y[i] * inv % MOD;
	}
}

void PolyMul(int* a, int* b, int n, int m) {  // n次多项式a乘m次多项式b,结果放在a中
	int lim = 1, len = 0;
	while (lim < (n + m << 1)) lim <<= 1, len++;
	for (int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1 | ((i & 1) << (len - 1)));

	NTT(a, lim, 1), NTT(b, lim, 1);
	for (int i = 0; i < lim; i++) a[i] = (ll)a[i] * b[i] % MOD;
	NTT(a, lim, -1);
}

int n;
int f[MAXN], derf[MAXN], invf[MAXN];  // f、f的导数、f的逆
int bell[MAXN];

int tmp[MAXN];  // 递归过程中临时存放系数的数组

void PolyInv(int deg, int* a, int* res) {
	if (deg == 1) {
		res[0] = qpow(a[0], MOD - 2, MOD);
		return;
	}

	PolyInv((deg + 1) >> 1, a, res);  // 递归求解

	int lim = 1, len = 0;
	while (lim < (deg << 1)) lim <<= 1, len++;

	for (int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1 | ((i & 1) << (len - 1)));

	for (int i = 0; i < lim; i++) tmp[i] = a[i] * (i < deg);  // i≥deg的项在模意义下为0

	NTT(tmp, lim, 1), NTT(res, lim, 1);
	for (int i = 0; i < lim; i++)
		res[i] = (ll)(2 - (ll)tmp[i] * res[i] % MOD + MOD) % MOD * res[i] % MOD;
	NTT(res, lim, -1);

	for (int i = deg; i < lim; i++) res[i] = 0;  // i≥deg的项在模意义下为0
}

void PolyDer(int deg, int* a, int* res) {  // 对deg次多项式a求导,结果放在res中
	for (int i = 1; i < deg; i++) res[i - 1] = (ll)i * a[i] % MOD;
	res[deg - 1] = 0;
}

void PolyInt(int deg, int* a, int* res) {  // 对deg次多项式a求不定积分(C=0),结果放在res中
	res[0] = 0;
	for (int i = 1; i < deg; i++) res[i] = (ll)a[i - 1] * qpow(i, MOD - 2, MOD) % MOD;
}

void PolyLn(int n, int* a, int* res) {  // 对n次多项式a求ln,结果放在res中
	memset(derf, 0, so(derf));
	memset(invf, 0, so(invf));

	int deg = 1;
	while (deg <= n) deg <<= 1;

	PolyDer(deg, a, derf);  // a的导数
	PolyInv(deg, a, invf);  // a的逆
	PolyMul(derf, invf, deg, deg);
	PolyInt(deg, derf, res);
}

int lnres[MAXN];  // res的ln
int tmpa[MAXN];  // 递归过程中临时存放系数的数组

void PolyExp(int deg, int* a, int* res) {  // 对deg次多项式a求exp,结果放在res中
	if (deg == 1) {
		res[0] = 1;
		return;
	}

	PolyExp(deg + 1 >> 1, a, res);

	PolyLn(deg, res, lnres);

	int lim = 1, len = 0;
	while (lim < (deg << 1)) lim <<= 1, len++;
	for (int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1 | ((i & 1) << (len - 1)));

	for (int i = 0; i < deg; i++) tmpa[i] = a[i];
	for (int i = deg; i < lim; i++) tmpa[i] = lnres[i] = 0;
	for (int i = 0; i < lim; i++) tmpa[i] = (((ll)tmpa[i] - lnres[i]) % MOD + MOD) % MOD;
	tmpa[0]++;

	NTT(tmpa, lim, 1), NTT(res, lim, 1);
	for (int i = 0; i < lim; i++) res[i] = (ll)res[i] * tmpa[i] % MOD;
	NTT(res, lim, -1);
	for (int i = deg; i < lim; i++) res[i] = 0;
}

int fac[MAXN], ifac[MAXN];

void init() {  // 预处理Bell数
	fac[0] = 1;
	for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;

	ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
	for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;

	n = 1e5 + 1;
	for (int i = 1; i < n; i++) f[i] = ifac[i];

	PolyExp(n, f, bell);

	for (int i = 0; i < n; i++) bell[i] = (ll)bell[i] * fac[i] % MOD;
}

int main() {
	init();  // 预处理Bell数
	
	CaseT{
		int n; cin >> n;
		int ans = 0;
		for (int i = 0; i <= n; i++) {
			int a; cin >> a;
			ans = ((ll)ans + (ll)a * bell[i]) % MOD;
		}
		cout << ans << endl;
	}
}


你可能感兴趣的:(2022ACM暑假训练,CCPC,ICPC补题,算法)