[題解] Atcoder AGC 005 F Many Easy Problems NTT,組合數學
阿新 • • 發佈:2022-05-07
題目
觀察當k固定時答案是什麼。先假設每個節點對答案的貢獻都是\(\binom{n}{k}\),然後再減掉某個點沒有貢獻的選點方案數。對於一個節點i,它沒有貢獻的方案數顯然就是所有k個節點都選在i連出去的某一個子樹內的方案數。列舉節點i,把i連出去的每一個子樹的size都加入一個序列c,則答案為\(\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\binom{c_i}{k}\)。
考慮\(k=1\cdots n\)的情況:
\(ans_k=\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\binom{c_i}{k}=\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\binom{c_i}{c_i-k}=\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\frac{c_i!}{k!(c_i-k)!}\)
\(-ans_k k!+\binom nk\cdot n\cdot k!=\sum_{i=0}^{|c|-1}\frac{c_i!}{(c_i-k)!}\)
統計每種\(c_i\)的出現次數後,右邊就變成了一個減法卷積的形式,一遍NTT即可。需要注意題目中的模數的原根是5.時間複雜度\(O(nlogn)\)。
點選檢視程式碼
#include <bits/stdc++.h> #define rep(i,n) for(int i=0;i<n;++i) #define repn(i,n) for(int i=1;i<=n;++i) #define LL long long #define pii pair <LL,LL> #define fi first #define se second #define mpr make_pair #define pb push_back using namespace std; const LL MOD=924844033; LL qpow(LL x,LL a) { LL res=x,ret=1; while(a>0) { if((a&1)==1) ret=ret*res%MOD; a>>=1; res=res*res%MOD; } return ret; } namespace poly { vector <LL> rev; void ntt(vector <LL> &a,LL G) { LL nn=a.size(),gn,g,x,y;vector <LL> tmp=a; rep(i,nn) a[i]=tmp[rev[i]]; for(int len=1;len<nn;len<<=1) { gn=qpow(G,(MOD-1)/(len<<1)); for(int i=0;i<nn;i+=(len<<1)) { g=1; for(int j=i;j<i+len;++j,(g*=gn)%=MOD) { x=a[j];y=a[j+len]*g%MOD; a[j]=(x+y)%MOD;a[j+len]=(x-y+MOD)%MOD; } } } } vector <LL> convolution(vector <LL> a,vector <LL> b,LL G) { LL nn=1,bt=0,sv=a.size()+b.size()-1;while(nn<a.size()+b.size()-1) nn<<=1LL,++bt; while(a.size()<nn) a.pb(0);while(b.size()<nn) b.pb(0); rev.clear(); rep(i,nn) { rev.pb(0); rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1)); } ntt(a,G);ntt(b,G); rep(i,nn) (a[i]*=b[i])%=MOD; ntt(a,qpow(G,MOD-2)); while(a.size()>sv) a.pop_back(); LL inv=qpow(nn,MOD-2); rep(i,a.size()) (a[i]*=inv)%=MOD; return a; } vector <LL> inverse(vector <LL> a,LL G) { if(a.size()==1) return vector <LL>{qpow(a[0],MOD-2)}; vector <LL> aa=a;while(aa.size()>(a.size()+1)>>1) aa.pop_back(); vector <LL> bb=inverse(aa,G); LL nn=1,bt=0,sv=a.size();while(nn<a.size()*2) nn<<=1LL,++bt; while(a.size()<nn) a.pb(0);while(bb.size()<nn) bb.pb(0); rev.clear(); rep(i,nn) { rev.pb(0); rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1)); } ntt(a,G);ntt(bb,G); rep(i,nn) a[i]=(2LL-a[i]*bb[i]%MOD+MOD)*bb[i]%MOD; ntt(a,qpow(G,MOD-2)); while(a.size()>sv) a.pop_back(); LL inv=qpow(nn,MOD-2); rep(i,a.size()) (a[i]*=inv)%=MOD; return a; } vector <LL> sqrt1(vector <LL> a,LL G)//常數項為1 { if(a.size()==1) return vector <LL>{1}; vector <LL> aa=a;while(aa.size()>(a.size()+1)>>1) aa.pop_back(); vector <LL> bb=sqrt1(aa,G);while(bb.size()<a.size()) bb.pb(0); vector <LL> bbb=inverse(bb,G); LL nn=1,bt=0,sv=a.size();while(nn<a.size()*2) nn<<=1LL,++bt; while(a.size()<nn) a.pb(0);while(bb.size()<nn) bb.pb(0);while(bbb.size()<nn) bbb.pb(0); rev.clear(); rep(i,nn) { rev.pb(0); rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1)); } LL mul=qpow(2,MOD-2); ntt(a,G);ntt(bb,G);ntt(bbb,G); rep(i,nn) a[i]=mul*(bb[i]+bbb[i]*a[i]%MOD)%MOD; ntt(a,qpow(G,MOD-2)); while(a.size()>sv) a.pop_back(); LL inv=qpow(nn,MOD-2); rep(i,a.size()) (a[i]*=inv)%=MOD; return a; } } LL n,sz[200010],fac[200010],inv[200010],ans[200010]; vector <LL> g[200010],A,B,C; LL CC(LL nn,LL mm){return fac[nn]*inv[mm]%MOD*inv[nn-mm]%MOD;} void dfs(LL pos,LL par) { sz[pos]=1; rep(i,g[pos].size()) if(g[pos][i]!=par) { dfs(g[pos][i],pos); sz[pos]+=sz[g[pos][i]]; ++A[sz[g[pos][i]]]; } if(pos!=1) ++A[n-sz[pos]]; } int main() { fac[0]=1;repn(i,200005) fac[i]=fac[i-1]*(LL)i%MOD; rep(i,200003) inv[i]=qpow(fac[i],MOD-2); cin>>n; LL x,y; rep(i,n-1) { scanf("%lld%lld",&x,&y); g[x].pb(y);g[y].pb(x); } rep(i,n) A.pb(0); dfs(1,0); rep(i,n) (A[i]*=fac[i])%=MOD; rep(i,n) B.pb(inv[i]); reverse(B.begin(),B.end()); C=poly::convolution(A,B,5); repn(i,n) { ans[i]=CC(n,i)*n%MOD; (ans[i]+=MOD-(i+n-1>=C.size() ? 0LL:C[i+n-1])*inv[i]%MOD)%=MOD; } repn(i,n) printf("%lld\n",ans[i]); return 0; }