題目連接
對於每一個\(k\),統計任選\(k\)個點做爲關鍵點的「最小生成樹」的大小之和
數組
正向想法是枚舉或者計算大小爲\(x\)、葉子數目爲\(y\)的子樹有多少種,而後貢獻答案。這種方法參數多、難統計,能夠感覺到沒法適應\(1e5\)的數據,捨棄
正難則反,自頂向下正向統計難,就考慮自底向上貢獻統計。那麼這裏的自底向上,就應該是對於每個點,統計其貢獻到每一個\(ans\)的次數,並累加。
既然要輸出k=1...m的答案,能夠猜到貢獻是一個卷積加速的形式
因此先考慮每一個點對某一個k的答案的貢獻
任選k個點以後,一個點對答案有1的貢獻,當且僅當選擇的點不全在以其爲根時的某棵子樹中
這個很好統計,不全在某棵子樹中這個條件很難考慮,不如直接用總數減去不合法的方案,畢竟全部元素用一個組合數就能夠搞定\({n \choose k}-\sum_v {size_v\choose k}\)
則
\[ ans_k=\sum_{u=1}^n{n \choose k}-\sum_{v\in \text{sub}_u}{size_v \choose k} \]
前一部分能夠直接算,但後一部分看起來不是一個數組的卷積
遇到這種狀況,咱們能夠用權值做爲下標先作一個統計數組\(a[size_v]++\),由於統計時使用的數據與這個\(size_v\)具體是哪個點的子樹大小關係不大,而只和子樹大小這個數值有關。所以不以每一個點做爲視角考慮(具體是誰不重要),而以整棵樹爲視角考慮,那麼\(ans_k\)就會變成
\[ ans_k=n{n\choose k}-\sum_{i=1}^{n-1}a_i{i \choose k} \]
減法卷積算出每一個\(ans_k\)的負部分便可ui
#include <cstdio> using namespace std; namespace IO{ const int S=10000000; char buf[S]; int pos; void load(){ fread(buf,1,S,stdin); pos=0; } char getChar(){ return buf[pos++]; } int getInt(){ int x=0,f=1; char c=getChar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getChar();} while('0'<=c&&c<='9'){x=x*10+c-'0';c=getChar();} return x*f; } } using IO::getInt; const int N=200005; const int MOD=924844033,G=5; int n; int h[N],tot; struct Edge{ int v,next; }e[N*2]; int size[N],sum[N]; int fact[N],iact[N]; inline void swap(int &x,int &y){ x^=y^=x^=y; } void addEdge(int u,int v){ e[++tot]=(Edge){v,h[u]}; h[u]=tot; e[++tot]=(Edge){u,h[v]}; h[v]=tot; } void readData(){ n=getInt(); int u,v; for(int i=1;i<n;i++){ u=getInt(); v=getInt(); addEdge(u,v); } } void dfs(int u,int fa){ size[u]=1; for(int i=h[u],v;i;i=e[i].next) if((v=e[i].v)!=fa){ dfs(v,u); size[u]+=size[v]; sum[size[v]]++; } sum[n-size[u]]++; } int fmi(int x,int y){ int res=1; for(;y;x=1ll*x*x%MOD,y>>=1) if(y&1) res=1ll*res*x%MOD; return res; } void init(){ fact[0]=1; for(int i=1;i<=n;i++) fact[i]=1ll*fact[i-1]*i%MOD; iact[0]=iact[1]=1; if(n>1){ iact[n]=fmi(fact[n],MOD-2); for(int i=n-1;i>=2;i--) iact[i]=1ll*iact[i+1]*(i+1)%MOD; } } inline int C(int n,int m){ return m>n?0:1ll*fact[n]*iact[m]%MOD*iact[n-m]%MOD; } namespace NTT{/*{{{*/ const int S=N*4,B=19; int n,invn,bit; int rev[S],W[S][2]; void build(){ int iG=fmi(G,MOD-2); for(int i=0;i<=B;i++){ W[1<<i][0]=fmi(G,(MOD-1)/(1<<i)); W[1<<i][1]=fmi(iG,(MOD-1)/(1<<i)); } } void init(int _n){ for(n=1,bit=0;n<_n;n<<=1,bit++); invn=fmi(n,MOD-2); for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1)); } void ntt(int *a,int f){ for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]); int w_n,w,u,v; for(int i=2;i<=n;i<<=1){ w_n=W[i][f]; for(int j=0;j<n;j+=i){ w=1; for(int k=0;k<(i>>1);k++){ u=a[j+k]; v=1ll*w*a[j+(i>>1)+k]%MOD; a[j+k]=(u+v)%MOD; a[j+(i>>1)+k]=(u-v)%MOD; w=1ll*w*w_n%MOD; } } } if(f) for(int i=0;i<n;i++) a[i]=1ll*a[i]*invn%MOD; } }/*}}}*/ void solve(){ static int a[NTT::S],b[NTT::S]; for(int i=0;i<n;i++){ a[i]=1ll*sum[i]*fact[i]%MOD; b[i]=iact[n-1-i]; } NTT::init(n+n-1); NTT::ntt(a,0); NTT::ntt(b,0); for(int i=0;i<NTT::n;i++) a[i]=1ll*a[i]*b[i]%MOD; NTT::ntt(a,1); // now a[n],a[n+1],... represent k=1,2,3,... int ans; for(int k=1;k<=n;k++){ ans=(1ll*n*C(n,k)%MOD-1ll*iact[k]*a[n-1+k]%MOD)%MOD; printf("%d\n",ans<0?ans+MOD:ans); } } int main(){ IO::load(); NTT::build(); readData(); dfs(1,0); init(); solve(); return 0; }