题目
你有一棵无根树(n个结点,1~n标号),和一个整数k。
对于一个树上的结点集合S,可以用一个子树把S中的点全部包含,这里子树指的是原树结点的一个子集,并且这些结点全部连通。对于集合S,令f(S)表示这样的子树最少所包含的结点数目。
共有C(n,k)种方法选出树上的k个结点。对于其中每种方法,设选出的点集为S,你需要求出所有这样的f(S)的总和。
答案可能很大,需要对924844033取模。 你需要对所有k=1~n都回答上述问题。
题解
考虑暴力怎么做。
最直接的想法,就是考虑一个点对
Ans[k]
A
n
s
[
k
]
的贡献。
则
Ans[k]=Σni=1(nk)−Σj∈son[i](siz[j]k)−[i≠1]∗(n−siz[i]k)
A
n
s
[
k
]
=
Σ
i
=
1
n
(
k
n
)
−
Σ
j
∈
s
o
n
[
i
]
(
k
s
i
z
[
j
]
)
−
[
i
≠
1
]
∗
(
k
n
−
s
i
z
[
i
]
)
然后
O(n2)
O
(
n
2
)
就能拿到40分了。
考虑答案式子。
Ans[k]=n∗Ckn−g[k]
A
n
s
[
k
]
=
n
∗
C
n
k
−
g
[
k
]
。
其中, cnt[k] c n t [ k ] 表示在原先的式子里 siz[k] s i z [ k ] 出现的次数。
设 f(i)=cnt[i]∗i! f ( i ) = c n t [ i ] ∗ i ! , h(i)=1i! h ( i ) = 1 i ! 。
则 k!∗g[k]=Σi≥kf(i)∗h(i−k) k ! ∗ g [ k ] = Σ i ≥ k f ( i ) ∗ h ( i − k ) 。
好像我看不出什么。
于是我在草稿纸上画了画,将 i i 看成。
于是整个式子又变成了 k!∗g[k]=Σi≥kf′(i)∗h(n−k−i) k ! ∗ g [ k ] = Σ i ≥ k f ′ ( i ) ∗ h ( n − k − i )
其中 f′(i)=f(n−i) f ′ ( i ) = f ( n − i ) 。
然后这就是卷积的形式了。
由于924844033的原根是5,所以就可以直接用 NTT N T T 。
代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#define N 200010
#define M 525000
#define LL long long
#define mo 924844033
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
int to,next;
};note edge[N*2];
int tot,head[N];
LL i,j,k,l,n,m,ans;
LL u,v,L,len;
LL jc[N],ny[N],fa[N];
LL siz[N],f[N],h[N];
LL f1[M],f2[M];
LL w[M],_w[M],Rev[M];
LL Ans[N];
LL read(){
LL fh=1,rs=0;char ch;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
return fh*rs;
}
void write(LL x){
if(x>9)write(x/10);
P(x%10+'0');
}
void lb(int x,int y){
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
}
LL ksm(LL x,LL y){
LL rs=1;
for(;y;y>>=1,x=(x*x)%mo)
if(y&1)rs=(rs*x)%mo;
return rs;
}
void pre_(){
int i;
jc[0]=jc[1]=ny[0]=ny[1]=1;
fo(i,2,N-10)jc[i]=(jc[i-1]*i)%mo;
ny[N-10]=ksm(jc[N-10],mo-2);
fd(i,N-11,2)ny[i]=(ny[i+1]*(i+1))%mo;
}
LL C(LL n,LL m){
if(n<m)return 0;
return ((jc[n]*ny[m])%mo*ny[n-m])%mo;
}
void add(LL &x,LL y){
x=(x+y+mo)%mo;
}
void dg(int x){
int i;
siz[x]=1;
for(i=head[x];i;i=edge[i].next)
if(fa[x]^edge[i].to){
fa[edge[i].to]=x;
dg(edge[i].to);
f[siz[edge[i].to]]++;
siz[x]+=siz[edge[i].to];
}
if(x^1)f[n-siz[x]]++;
}
void NTT(LL *y,LL *w){
LL i,h,k;
fo(i,0,L-1)
if(i<Rev[i])
swap(y[i],y[Rev[i]]);
for(h=2;h<=L;h<<=1){
for(i=0;i<L;i+=h){
fo(k,i,i+(h>>1)-1){
LL u=y[k],t=(w[L/h*(k-i)]*y[k+(h>>1)])%mo;
y[k]=(u+t)%mo;
y[k+(h>>1)]=((u-t)%mo+mo)%mo;
}
}
}
}
int main(){
n=read();
fo(i,1,n-1){
u=read();v=read();
lb(u,v);lb(v,u);
}
pre_();
dg(1);
fo(i,1,n)f[i]=(f[i]*jc[i])%mo;
fo(i,0,n)h[i]=ny[i];
len=0;
for(L=1;L<(n+1)*2;L<<=1)len++;
w[0]=w[L]=1;
w[1]=ksm(5,(mo-1)/L);
fo(i,1,L-1) w[i]=w[i-1]*1ll*w[1]%mo;
fo(i,0,L) _w[i]=w[L-i];
fo(i,0,L-1)Rev[i]=Rev[i>>1]>>1|(i&1)<<len-1;
fo(i,0,n)f1[i]=f[n-i];
fo(i,n+1,L-1)f1[i]=0;
fo(i,0,n)f2[i]=h[i];
fo(i,n+1,L-1)f2[i]=0;
NTT(f1,w);
NTT(f2,w);
fo(i,0,L-1)f1[i]=f1[i]*f2[i]%mo;
NTT(f1,_w);
LL inv=ksm(L,mo-2);
fo(i,0,L-1)f1[i]=(f1[i]*inv)%mo;
fo(i,1,n)Ans[i]=(f1[n-i]*ny[i])%mo;
fo(i,1,n)Ans[i]=(n*C(n,i)%mo-Ans[i]+mo)%mo;
fo(i,1,n)write(Ans[i]),P('\n');
return 0;
}