【P5401 [CTS2019] 珍珠】题解

· · 个人记录

本题解参考 cmd 大佬的题解。

时间复杂度为 O(D),是严格的线性,目前 2022.3.11 是最优解。

为了方便先改变一下记号,记 n 为颜色数(随机变量的范围,也就是 D),L 为珍珠数(随机变量个数),m 为瓶子数。

至少装满 m 个瓶子相当于至少配对 m 对珍珠。

容易证明,以下两种情况是平凡的

所以接下来讨论的情况都是 0\le L-2m\le n-2

设有 i 种颜色出现了奇数个珍珠(称作奇颜色),有 n-i 种颜色出现了偶数个珍珠(称作偶颜色)。

这样仅有 i 个珍珠是孤立的,其它珍珠一定能配对。

所以配对的个数就是 \displaystyle \frac{L-i}{2},至少配对 m 对珍珠相当于 i\le L-2m

也就是说只要满足 0\le i\le L-2m,方案一定合法。

使用 \text{EGF},奇颜色和偶颜色的 \text{EGF} 可分别表示为

\begin{aligned} \frac{e^x+e^{-x}}{2}\\ \frac{e^x-e^{-x}}{2} \end{aligned}

所以有 i 种奇颜色,n-i 种偶颜色的情况就可以表示为

L![x^L]\left(\frac{e^x+e^{-x}}{2}\right)^{n-i}\left(\frac{e^x-e^{-x}}{2}\right)^{i}

枚举奇颜色的个数,最终答案就是

ans=\sum_{i=0}^{L-2m}{n\choose i}L![x^L]\left(\frac{e^x+e^{-x}}{2}\right)^{n-i}\left(\frac{e^x-e^{-x}}{2}\right)^{i}

化简一番

\begin{aligned} ans&=\frac{1}{2^n}\sum_{i=0}^{L-2m}{n\choose i}L![x^L](e^x+e^{-x})^{n-i}(e^x-e^{-x})^{i}\\ &=\frac{1}{2^n}\sum_{i=0}^{L-2m}{n\choose i}L![x^L]\sum_{j=0}^{n-i}\sum_{k=0}^{i}{n-i\choose j}{i\choose k}(-1)^{i-k}e^{jx}e^{(i+j-n)x}e^ke^{(k-i)x}\\ &=\frac{1}{2^n}\sum_{i=0}^{L-2m}{n\choose i}L![x^L]\sum_{j=0}^{n-i}\sum_{k=0}^{i}{n-i\choose j}{i\choose k}(-1)^{i-k}e^{(2j+2k-n)x}\\ &=\frac{1}{2^n}\sum_{i=0}^{L-2m}{n\choose i}\sum_{j=0}^{n-i}\sum_{k=0}^{i}{n-i\choose j}{i\choose k}(-1)^{i-k}(2j+2k-n)^{L}\\ &=\frac{1}{2^n}\sum_{i=0}^{L-2m}{n\choose i}\sum_{r=0}^{n}(2r-n)^{L}\sum_{k=0}^{r}{n-i\choose r-k}{i\choose k}(-1)^{i-k}\\ &=\frac{1}{2^n}\sum_{i=0}^{L-2m}{n\choose i}\sum_{r=0}^{n}(2r-n)^{L}[x^r](x-1)^{i}(x+1)^{n-i}\\ &=\frac{1}{2^n}\sum_{r=0}^{n}(2r-n)^{L}[x^r]\sum_{i=0}^{L-2m}{n\choose i}(x-1)^{i}(x+1)^{n-i}\\ \end{aligned}

不妨设 k=L-2m,生成函数 f(x) 满足

f(x)=\sum_{i=0}^{k}{n\choose i}(x-1)^{i}(x+1)^{n-i}\tag{*0}

所以

ans=\frac{1}{2^n}\sum_{r=0}^{n}(2r-n)^Lf[r]

于是问题的关键在于求解函数 f(x)

因为 k\neq n,很难通过二项式定理等普通的方法去掉 f(x) 中的求和式,于是我们尝试通过求导得出 f(x) 的封闭形式(也就是去掉了求和符号的等式),再通过此求出 f(x) 的递推式。

然而 f(x) 的形式较复杂,我们先求出两个形式更简单的生成函数的封闭形式,以此为基础再求出 f(x) 的封闭形式。

首先设

g(x)=\sum_{i=0}^{k}{n\choose i}A(x)^i\tag{*1}

求导得

g'(x)=A'(x)\sum_{i=0}^{k}{n\choose i}iA(x)^{i-1}\tag{*2}

变换形式得

\begin{aligned} g'(x)&=A'(x)\sum_{i=1}^{k}{n\choose i-1}(n-i+1)A(x)^{i-1}\\ &=nA'(x)\sum_{i=1}^{k}{n\choose i-1}A(x)^{i-1}-A'(x)\sum_{i=1}^{k}{n\choose i-1}(i-1)A(x)^{i-1}\\ &=nA'(x)\sum_{i=0}^{k-1}{n\choose i}A(x)^{i}-A'(x)\sum_{i=0}^{k-1}{n\choose i}iA(x)^{i}\\ \end{aligned}\tag{*3}

(\text{*1})(\text{*2}) 综合到 (\text{*3}) 上,得出

g'(x) =nA'(x)\left(g(x)-{n\choose k}A(x)^{k}\right)-\left(A(x)g'(x)-A'(x){n\choose k}kA(x)^{k}\right)

整理后得到

nA'(x)g(x)-(A(x)+1)g'(x)={n\choose k}(n-k)A'(x)A(x)^k\tag{*4}

再设

h(x)=\sum_{i=0}^{k}{n\choose i}A(x)^iB(x)^{n-i}

我们现在求出 h(x) 的封闭形式。

\frac{h(x)}{B(x)^{n}}=\sum_{i=0}^{k}{n\choose i}\left(\frac{A(x)}{B(x)}\right)^i\tag{*5}

注意到 (\text{*5}) 的形式与 (\text{*1}) 相同,我们直接利用 (\text{*4}) 的结果。

\left(\frac{h(x)}{B(x)^{n}}\right)'=\frac{h'(x)B(x)-nh(x)B'(x)}{B(x)^{n+1}}\\ \left(\frac{A(x)}{B(x)}\right)'=\frac{A'(x)B(x)-A(x)B'(x)}{B(x)^2}

将上面两个求导式带入 (\text{*4}),同时在等式左右两边乘上 B(x)^{n+2} 后得

n(A'B-AB')h-(A+B)(h'B-nhB')={n\choose k}(n-k)(A'B-AB')A^kB^{n-k}

式子太长了,就先省略 (x) 了。

整理后得到

nB(A'+B')h-B(A+B)h'={n\choose k}(n-k)(A'B-AB')A^kB^{n-k}

两边同时除以 B 得到最终形式

n(A'+B')h-(A+B)h'={n\choose k}(n-k)(A'B-AB')A^kB^{n-k-1}\tag{*6}

重新对照 h(x) 的表达式 (\text{*5})f(x) 的表达式 (\text{*0}) 得到

A(x)=x-1,\ B(x)=x+1

带入 (\text{*6})

nf(x)-xf'(x)={n\choose k}(n-k)(x-1)^k(x+1)^{n-k-1}\tag{*7}

于是我们去掉了求和号,得到了 f(x) 的封闭形式。

然而 (\text{*7}) 仍不能很好的帮助我们求出 f(x) 的递推式,还需要更进一步。

a>0,b>0\ (a,b\in\mathbb{Z}),生成函数 p(x) 满足

p(x)=(x-1)^a(x+1)^b

则有

\begin{aligned} p'(x)=\frac{a}{x-1}p(x)+\frac{b}{x+1}p(x)\\ x^2p'(x)-p'(x)=(a+b)xp(x)+(a-b)p(x) \end{aligned}

a=k,\ b=n-k-1,得到

x^2p'(x)-p'(x)=(n-1)xp(x)+(2k+1-n)p(x)

这显然可以求出递推式,于是当 i\ge 2 时有

\begin{aligned}[] [x^{i-1}](x^2p'(x)-p'(x))=[x^{i-1}]((n-1)xp(x)+(2k+1-n)p(x))\\ (i-2)p[i-2]-ip[i]=(n-1)p[i-2]+(2k+1-n)p[i-1]\\ p[i]=\frac{1}{i}(n-2k-1)p[i-1]-\frac{1}{i}(n-i+1)p[i-2] \end{aligned}\tag{*8}

直接观察 p(x) 的定义是,可以得到递推的边界条件为

p[0]=(-1)^k,\ p[1]=(-1)^k(n-2k-1)

(\text{*7})

nf(x)-xf'(x)={n\choose k}(n-k)p(x)

所以当 0\le i\le n 时有

(n-i)f[i]={n\choose k}(n-k)p[i]

对照 (\text{*8}) 得,当 2\le i\le n-1 时有

f[i]=\frac{n-i+1}{i(n-i)}\left((n-2k-1)f[i-1]-(n-i+2)f[i-2]\right)\tag{*9}

ps: 多么丑陋的式子啊!

递推的边界条件是

\begin{aligned} &f[0]=\frac{g[0]}{n}=(-1)^k{n-1\choose k}\\ &f[1]=\frac{g[1]}{n-1}=(-1)^k\frac{n-k}{n-1}{n\choose k} \end{aligned}\tag{*10}

其次,直接观察 f(x) 定义式 (\text{*0}) 可得

\begin{aligned} f[n]&=[x^n]\sum_{i=0}^{k}{n\choose i}(x-1)^{i}(x+1)^{n-i}\\ &=\sum_{i=0}^{k}{n\choose i} \end{aligned}\tag{*11}

利用 (\text{*9}),(\text{*10}),(\text{*11}),我们可以以 O(n) 求出 f[i]\ (0\le i\le n)

回看答案的计算式

ans=\frac{1}{2^n}\sum_{r=0}^{n}(2r-n)^Lf[r]

t(i)=i^L\ (0\le i\le n),注意到 t 是完全积性函数,对素数进行快速幂,用线性筛可以以 O(n\log n/\ln n)=O(n) 预处理出 t(i)

因此可以 O(n) 求出 ans

以下是代码。 ```cpp #include <stdio.h> #include <algorithm> #include <string.h> #include <iostream> #include <assert.h> using namespace std; #define re register #define sf scanf #define pf printf #define nl() putchar('\n') #define ll unsigned long long #define db double #define _for(i, a, b) for(re int i = (a); i < (b); ++i) #define _rfor(i, a, b) for(re int i = (a); i <= (b); ++i) #define maxn 100005 #define mod 998244353ll int rdnt(){ re int x = 0, sign = 1; re char c = getchar(); while (c < '0' || c > '9') { if (c == '-') sign = -1; c = getchar(); } while (c >= '0' && c <= '9') x = (x<<3) + (x<<1) + (c ^ 48), c = getchar(); return x * sign; } int n, L, m, k, pcnt = 0, f[maxn], t[maxn], prime[maxn], iv[maxn], fct[maxn], ivf[maxn]; bool vis[maxn]; int fp(re int x, re int n){re int y=1;for(;n;x=(ll)x*x%mod,n>>=1)if(n&1)y=(ll)x*y%mod;return y;} int get_inv(re int a){ return fp(a, mod-2); } int C(re int n, re int m){ return (ll)fct[n]*ivf[m]%mod*ivf[n-m]%mod; } void init(re int n){ t[1] = iv[1] = fct[0] = fct[1] = ivf[0] = ivf[1] = 1; t[0] = 0; _rfor(i, 2, n){ if (!vis[i]) prime[++pcnt] = i, t[i] = fp(i, L), iv[i] = get_inv(i); fct[i] = (ll)fct[i-1]*i%mod; ivf[i] = (ll)ivf[i-1]*iv[i]%mod; for(re int j = 1; j <= pcnt && i*prime[j] <= n; ++j){ vis[i*prime[j]] = true; iv[i*prime[j]] = (ll)iv[i]*iv[prime[j]]%mod; t[i*prime[j]] = (ll)t[i]*t[prime[j]]%mod; if (i%prime[j] == 0) break; } } } int main(){ #ifndef ONLINE_JUDGE freopen("sample.in", "r", stdin); //freopen("sample.out", "w", stdout); #endif n = rdnt(), L = rdnt(), m = rdnt(), k = L-2*m; if (k > n-2){ pf("%d\n", fp(n, L)); return 0; } if (k < 0){ pf("0\n"); return 0; } init(n); f[0] = (ll)C(n-1, k)%mod; if (k&1) f[0] = (mod-f[0])%mod; f[1] = (ll)C(n, k)*(n-k)%mod*(n-2*k-1+mod)%mod*iv[n-1]%mod; if (k&1) f[1] = (mod-f[1])%mod; _rfor(i, 0, k) f[n] = (f[n] + C(n, i))%mod; _rfor(i, 2, n-1){ f[i] = ( (ll)(mod+n-2*k-1)*f[i-1]%mod - (ll)(n-i+2)%mod*f[i-2]%mod + mod )%mod*iv[n-i]%mod*iv[i]%mod*(n-i+1)%mod; } re int ans = 0, j; _rfor(i, 0, n){ j = 2*i>=n ? t[2*i-n] : (L&1 ? mod-t[n-2*i] : t[n-2*i]); //assert(j == fp((mod+2*i-n)%mod, L)); ans = (ans + (ll)j*f[i]%mod)%mod; } ans = (ll)fp(iv[2], n)*ans%mod; pf("%d\n", ans); return 0; } ```