题解 P5401 【[CTS2019]珍珠】

NaCly_Fish

2020-05-30 23:17:46

Solution

djq 提到了一种时间复杂度更优的解法,看到洛谷题解区还没有,就来复读一遍吧( **** 省略亿点步骤,题目中要求的可以转化为如下问题: $$\frac{n!}{2^d}[x^n]\sum_{i=0}^k \binom di(\text e^x-\text e^{-x})^i(\text e^x+\text e^{-x})^{d-i}$$ 其中 $k = \min(d,n-2m)$。 由于 EGF 不太好搞,而关于 $x$ 的式子只有 $\text e^x$ 这样的,于是考虑转为 OGF: $$F(x)=\sum_{i=0}^k \binom di(x-x^{-1})^i(x+x^{-1})^{d-i}$$ $$=x^{-d}\sum_{i=0}^k \binom di (x^2-1)^i(x^2+1)^{d-i}$$ 这样要求的就是 $[x^n]F(\text e^x)$,但形如 $(x^2+1)$ 的形式还是不好看,改为: $$G(x)=\sum_{i=0}^k \binom di(x-1)^i(x+1)^{d-i}$$ 然后答案就能写成 $$\frac{n!}{2^d}[x^n]G(\text e^{2x})\text e^{-dx}$$ 只要求出 $G(x)$ 的系数,那么其卷积的 $n$ 次项就能直接求出。 具体来讲,答案可以写为 $$2^{-d}\sum_{i=0}^dg_i (2i-d)^n$$ 线性筛求幂,是 $\Theta(d \log_d n)$ 的。 然后使用万能的(并不)求导大法,就能推出: $$G'(x)=\sum_{i=0}^k \binom di(i(x-1)^{i-1}(x+1)^{d-i}+(d-i)(x-1)^i(x+1)^{d-i-1})$$ $$2dG(x)-2xG'(x)=d\binom{d-1}{k}((x-1)^k(x+1)^{d-k}-(x-1)^{k+1}(x+1)^{d-k-1})$$ $$dG(x)-xG'(x)=d\binom{d-1}{k}(x-1)^k(x+1)^{d-k-1}$$ 设 $H(x)=dG(x)-xG'(x)$,就可以直接提取系数 $(d-n)g_n=h_n$ 计算 $G(x)$ 的系数(注意,$h_d$ 为零,而 $g_d$ 显然不为零,所以要单独算一下)。 要算 $H(x)$ 很简单,只要能快速求形如 $$Q(x)=(x+1)^a(x-1)^b$$ 的系数就好了。 $$Q'(x)=a(x+1)^{a-1}(x-1)^b+b(x+1)^a(x-1)^{b-1}$$ $$= \frac{aQ(x)}{x+1}+\frac{bQ(x)}{x-1}$$ $$(x^2-1)Q'(x)=a(x-1)Q(x)+b(x+1)Q(x)$$ $$-(n+1)q_{n+1}=a(q_{n-1}-q_n)+b(q_{n-1}+q_n)-(n-1)q_{n-1}$$ 显然可以 $\Theta(d)$ 递推计算前 $d$ 项。 总时间复杂度 $\Theta(d \log_d n)$。 ```cpp #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #define N 100003 #define ll long long #define reg register #define p 998244353 using namespace std; inline int add(const int& x,const int& y){ return x+y>=p?x+y-p:x+y; } inline int power(int a,int t){ int res = 1; while(t){ if(t&1) res = (ll)res*a%p; a = (ll)a*a%p; t >>= 1; } return res; } int inv[N],pw[N],pr[N>>2],fac[N],ifac[N]; void sieve(int n,int k){ int cnt = 0; fac[0] = fac[1] = ifac[0] = ifac[1] = inv[1] = pw[1] = 1; for(reg int i=2;i<=n;++i){ inv[i] = (ll)(p-p/i)*inv[p%i]%p; fac[i] = (ll)fac[i-1]*i%p; if(!pw[i]){ pr[++cnt] = i; pw[i] = power(i,k); } for(reg int j=1;j<=cnt&&i*pr[j]<=n;++j){ pw[i*pr[j]] = (ll)pw[i]*pw[pr[j]]%p; if(i%pr[j]==0) break; } } ifac[n] = power(fac[n],p-2); for(reg int i=n-1;i;--i) ifac[i] = (ll)ifac[i+1]*(i+1)%p; } inline int binom(int n,int m){ return (ll)fac[n]*ifac[m]%p*ifac[n-m]%p; } int d,n,k,ans,sgn,a,b,bm; int g[N],q[N]; int main(){ scanf("%d%d%d",&d,&n,&k); if((k<<1)<=n-d){ printf("%d",power(d,n)); return 0; }else if((k<<1)>n){ putchar('0'); return 0; } k = min(d,n-(k<<1)); sgn = n&1?-1:1; sieve(d+1,n); bm = (ll)d*binom(d-1,k)%p; a = d-k-1,b = k; q[0] = b&1?-1:1,q[1] = (b&1?-1:1)*a+((b-1)&1?-b:b); for(reg int i=1;i!=d;++i) q[i+1] = -((ll)a*(q[i-1]-q[i])%p+(ll)b*(q[i-1]+q[i])-(ll)(i-1)*q[i-1])%p*inv[i+1]%p; for(reg int i=0;i!=d;++i) g[i] = (ll)q[i]*bm%p*inv[d-i]%p; for(reg int i=0;i<=k;++i) g[d] = add(g[d],binom(d,i)); for(reg int i=0;i<=d;++i){ if((i<<1)<d) ans = (ans+(ll)g[i]*sgn*pw[d-(i<<1)])%p; else ans = (ans+(ll)g[i]*pw[(i<<1)-d])%p; } ans = (ll)ans*power(2,p-1-d)%p; printf("%d",ans<0?ans+p:ans); return 0; } ```