题解 P4705 【玩游戏】

WinXP

2018-07-02 16:17:54

Solution

基础前置技能 1.基础数学 2.NTT(FFT) 3.多项式求逆 4\*.Taylor展开 设 $ans_k$,为 $$ans_k=\sum_{i=1}^n \sum_{j=1}^m (a_i+b_j)^k$$ 设 $Ans(x)$ 为 $ans$ 的生成函数 答案即为求多项式 $Ans(x) * \frac{1}{nm}$ 然后....首先这个式子.... $$ans_k= \sum_{i=1}^n \sum_{j=1}^m (a_i+b_j)^k$$ 考虑二项式展开 $$ans_k= \sum_{i=1}^n \sum_{j=1}^m \sum_{t=0}^k C_k^t a_i^{k-t} b_j^t$$ 考虑交换求和顺序,把 $k$ 提到前面 $$ans_k= \sum_{t=0}^k \sum_{i=1}^n \sum_{j=1}^m C_k^t a_i^{k-t} b_j^t$$ $$ans_k= \sum_{t=0}^k C_k^t \sum_{i=1}^n a_i^{k-t}\sum_{j=1}^m b_j^t$$ 可以发现,如果计两个新的多项式为 $A(x),B(x)$,令 $$A(x)=\sum_{i=1}^n a_i^x$$ $$B(x)=\sum_{i=1}^m b_i^x$$ 就可以得到一个很优美的化简: $$ans_k= \sum_{t=0}^k C_k^t\ A(k-t)\ B(t)$$ 组合数直接拆出来 $$ans_k= \sum_{t=0}^k \frac{k!}{t!(k-t)!}\ A(k-t)\ B(t)$$ $$=k! \sum_{t=0}^k \frac{A(k-t)}{(k-t)!}\ \frac{B(t)}{t!}$$ 非常好。现在的问题变成了$A(x)=\sum_{i=1}^n a_i^x$这个看起来非常不友好的多项式怎么求。~~(说实话不会的话真的是没法想出来的...)(Drench说是非常经典的问题)~~ 先考虑一个多项式,即为$F(x)$,为 $$F(x)=\prod_{i=1}^n(a_ix+1)$$ 这个东西可以分治求出来。具体一点大概就是... $$F_l(x)=\prod_{i=1}^{(n/2)}\ (a_ix+1)$$ $$F_r(x)=\prod_{i=(n/2)+1}^{n}(a_ix+1)$$ $$F(x)=F_l(x)F_r(x)$$ 求出这个东西之后,再对这个东西取$ln$.... $$ln(F(x))=G(x)$$ 这是个多项式求$ln$,具体来说... $$(ln(x))'=\frac{1}{x}$$ 链式法则:如果 $$h(x)=f(g(x))$$ 那么就会有 $$h'(x)=f'(g(x))g'(x)$$ 这里的$ln(x)$就相当于$f(x)$ 因为$G(x)=f(F(x))$ $$G'(x)=f'(F(x))F'(x)=\frac{F'(x)}{F(x)}$$ 可能需要反应一下 然后, $$G(x)=\int \frac{F'(x)}{F(x)}$$ 至于多项式的求导和积分,其实非常简单,求导的时候$a_{i}=(i+1)a^{(i+1)},$ 积分的时候$a_i=inv(i)a_{i-1}$。关键是求出$\frac{1}{F(x)}$是一个多项式求逆的过程。[P4238 【模板】多项式求逆](https://www.luogu.org/problemnew/show/P4238) 这么费劲求出 $G(x)$ 干嘛呢? ...... 泰勒($taylor$)展开: 首先约定用$f^{(n)}(x)$表示$f(x)$的$n$阶导数。 若函数 $f(x)$ 在包含 $x0$ 的某个闭区间 $[a,b]$ 上具有 $n$ 阶导数,且在开区间 $(a,b)$ 上具有 $(n+1)$ 阶导数,则对闭区间 $[a,b]$ 上任意一点 $x$ ,成立下式: ![](https://gss1.bdstatic.com/9vo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D561/sign=143f6442fadeb48fff69a1d8c11e3aef/a686c9177f3e670920f8bbdc32c79f3df8dc551d.jpg) 上面是百度百科抄的~~(顺手引用了图片。)~~。 用非常不准确的人话来说,就是有一个函数$f(x)$,这个函数随便是什么$log,sin,cos$,只要这个$f(x)$可在任意点导任意次,我们就可以用一个非常奇怪的无穷项的多项式$g(x)$从某一个点$x_0$来拟合这样的一个函数。$g(x)$的每一项$a_i$为: $$\frac{f^{(n)}(x_0)}{n!}$$ 合起来就是 $$f(x)=\sum_{k=1}^{+\infty} \frac{f^{(k)}(x_0)}{k!}(x-x_0)^k$$ 这个东西的大致思路就是让$g(x_0)=f(x_0)$,让$g'(x_0)=f'(x_0)$,让$g''(x_0)=f''(x_0)$,直到让$g^{(+\infty)}(x_0)=f^{(+\infty)}(x_0)$,这两个函数就可以说一样了。满足这个条件的$g(x)$写出来就是泰勒展开。当然没有这么简单,关于taylor展开知乎上有讲的非常好的,如果想学建议自己去看一看。 回到这个东西 $F(x)=\prod_{i=1}^n(a_ix+1)$ 。用分治NTT计算出 $F(x)$ 之后再对它多项式求$ln$得到 $G(x)$ : $$G(x)=ln(\ \ \prod_{i=1}^n(a_ix+1)\ \ )$$ 非常明显的化简 $$G(x)=\sum_{i=1}^nln(a_ix+1)$$ 然后我们考虑把里面的这个函数 $ln(x)$ 在 $1$ 这个点进行Taylor展开。 原始的公式 $$f(x)=\sum_{k=1}^{+\infty} \frac{f^{(k)}(x_0)}{k!}(x-x_0)^k$$ $ln(x)$求导的通项公式 $$(ln(x))'=x^{-1}\ \ \ \ \ \ \ (x^a)'=(a-1)x^{a-1}$$ $$(ln(x))^{(n)}=(-1)^{(n+1)}n!x^{-n}$$ 带入 $$ln(a_ix+1)=\sum_{k=1}^{+\infty} \frac{(-1)^{(k+1)}(k-1)!}{k!}(a_ix)^k$$ $$ln(a_ix+1)=\sum_{k=1}^{+\infty} \frac{(-1)^{(k+1)}}{k}a_i^kx^k$$ 因为在求$G(x)$的时候用到了多项式求逆,对$x^{K+1}$取模后,这个式子中大于$K$次的项没有意义. 对于单独的一项 $$ln(a_ix+1)=\sum_{k=1}^{K} \frac{(-1)^{(k+1)}}{k}a_i^kx^k$$ 所以 $$G(x)=\sum_{i=1}^nln(a_ix+1)=\sum_{i=1}^n\sum_{k=1}^{K} \frac{(-1)^{(k+1)}}{k}a_i^kx^k$$ 为了表达的清楚一点随便找一个$t$把$t(k)$记为$\frac{(-1)^{(k+1)}}{k}$ $$G(x)=\sum_{i=1}^nln(a_ix+1)=\sum_{i=1}^n\sum_{k=1}^{K} t(k)a_i^kx^k$$ 显然可以交换一下顺序 $$G(x)=\sum_{k=1}^{K} t(k)\sum_{i=1}^na_i^kx^k$$ $$G(x)=\sum_{k=1}^{K} t(k)A(k)x^k$$ 哇! 然后我的代码写的巨型丑,常数莫名巨型大,卡了一次才A,仅供对拍... ```cpp #include <bits/stdc++.h> #define rap(i,s,n) for(int i=s;i<=n;i++) #define drap(i,n,s) for(int i=n;i>=s;i--) #define N 410000 #define P 998244353 #define lb(x) ((x&(-x))) #define ll long long #define m(s,k) memset(s,k,sizeof s) using namespace std; char xB[1<<15],*xS=xB,*xTT=xB; #define getc() (xS==xTT&&(xTT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xTT)?0:*xS++) #define isd(c) ((c>='0'&&c<='9')||(c=='-')) template<typename T> inline bool rd(T& xa){ char xchh; T f=1; while(xchh=getc(),(!isd(xchh))&&(xchh!=0)); if(xchh==0) return 0; if(xchh=='-') xchh=getc(),f=-1; xa=xchh-'0'; while(xchh=getc(),isd(xchh)) xa=xa*10+xchh-'0'; xa*=f; return 1; } ll mpow(ll a,ll k,ll p){ll res=1; while(k){if(k&1) res=res*a%p; k>>=1; a=a*a%p;} return res%p;} ll Inv(ll x){return mpow(x,P-2,P);} ll fac[N],inv[N],finv[N]; void Finit(int n){ fac[0]=1; rap(i,1,n) fac[i]=fac[i-1]*i%P; inv[1]=1; rap(i,2,n) inv[i]=inv[P%i]*(P-P/i)%P; finv[0]=1; rap(i,1,n) finv[i]=finv[i-1]*inv[i]%P; } namespace Poly{ ll c[N],f[N],f1[N],fi[N]; int rev[N],lim,l; void init(int n){ lim=1,l=0; while(lim<=n) lim<<=1,l++; rap(i,1,lim-1) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1)); } void NTT(ll *A,int k){ rap(i,1,lim-1) if(i<rev[i]) swap(A[i],A[rev[i]]); for(int l=1;l<lim;l<<=1){ ll gn=mpow(3,(P-1)+k*(P-1)/l/2,P); for(int j=0;j<lim;j+=l*2){ ll g=1; for(int k=0;k<l;k++,g=g*gn%P){ ll x=A[k+j],y=A[k+j+l]*g%P; A[k+j]=(x+y)%P; A[k+j+l]=(x-y+P)%P; } } } if(k==-1) {ll tinv=Inv(lim); rap(i,0,lim-1) A[i]=A[i]*tinv%P;} } void MUL(ll *A,int n,ll *B,int m,ll *C){ init(n+m); NTT(A,1); NTT(B,1); rap(i,0,lim-1) C[i]=A[i]*B[i]%P; NTT(A,-1); NTT(B,-1); NTT(C,-1); } void MUL(ll *A,int n,ll *B,int m){ init(n+m); NTT(A,1); NTT(B,1); rap(i,0,lim-1) B[i]=B[i]*A[i]%P; NTT(A,-1); NTT(B,-1); } void INV(ll *A,ll *B,int n){ if(n==1){B[0]=Inv(A[0]); return;} INV(A,B,(n+1)>>1); init(2*n); m(c,0); rap(i,0,n-1) c[i]=A[i]; NTT(c,1); NTT(B,1); rap(i,0,lim-1) B[i]=(2-B[i]*c[i]+P)%P*B[i]%P; NTT(B,-1); rap(i,n,lim-1) B[i]=0; return; } void MUL(ll *A,int *a,int n){ int len=1; while(len<n) len<<=1; ll *f[N]; rap(i,1,len){f[i]=new ll[lb(i)<<2]; f[i][0]=1; f[i][1]=a[i];} for(int l=1;l<len;l<<=1){init(2*l); for(int j=l*2;j<=len;j+=l*2){ NTT(f[j-l],1); NTT(f[j],1); rap(i,0,lim-1) f[j][i]=f[j][i]*f[j-l][i]%P; NTT(f[j],-1); }} rap(i,0,n) A[i]=f[len][i]; return; } void DEAL(ll *A,int *a,int n,int k){ m(f,0); MUL(f,a,n); m(f1,0); rap(i,0,n-1) f1[i]=f[i+1]*(i+1)%P; m(fi,0); INV(f,fi,k+1); MUL(f1,n-1,fi,k,A); drap(i,k,1) A[i]=A[i-1]*inv[i]%P; rap(i,k+1,lim-1) A[i]=0; rap(i,1,k){A[i]=A[i]*i%P; if((i&1)==0) A[i]=P-A[i];} } } int n,m,K,a[N],b[N]; ll A[N],B[N],ans[N]; int main(){ rd(n); rd(m); rap(i,1,n) rd(a[i]); rap(i,1,m) rd(b[i]); rd(K); Finit(max(max(n,m),K)+100); Poly::DEAL(A,a,n,K); Poly::DEAL(B,b,m,K); rap(i,1,K) A[i]=A[i]*finv[i]%P,B[i]=B[i]*finv[i]%P; A[0]=n; B[0]=m; Poly::MUL(A,K,B,K,ans); ll tinv=Inv(1ll*n*m%P); rap(i,1,K) ans[i]=ans[i]*fac[i]%P*tinv%P; rap(i,1,K) printf("%lld\n",ans[i]); return 0; } ```