分析:
这题最恶心的一点就在开头:
首先,在树上,联通块大小为边的大小+1,所以可以算边的贡献:
对每条边而言,如果它能造成贡献,那么必然在它两端都有选中的点,设选k个点,那么方案数就是 C ( n , k ) − C ( s u m a , k ) − C ( s u m b , k ) C(n,k)-C(sum_a,k)-C(sum_b,k) C(n,k)−C(suma,k)−C(sumb,k), s u m a , s u m b sum_a,sum_b suma,sumb分别指这两侧的联通块大小。
然后再进一步,算每个分出来的联通块的贡献,
a
n
s
k
=
n
∗
C
(
n
,
k
)
−
∑
i
=
k
i
≤
N
c
n
t
i
∗
C
(
i
,
k
)
ans_k=n*C(n,k)-\sum_{i=k}^{i\leq N}cnt_i*C(i,k)
ansk=n∗C(n,k)−∑i=ki≤Ncnti∗C(i,k)
其中
c
n
t
i
cnt_i
cnti表示像上面说的那样,通过断一条边形成的联通块大小为i的数量。
这样就有 O ( n 2 ) O(n^2) O(n2)的算法了。
然后就有点套路化了,由于这里涉及到组合数,不妨把它拆开:
a
n
s
k
=
n
∗
n
!
k
!
∗
(
n
−
k
)
!
−
∑
i
=
k
i
≤
N
c
n
t
i
∗
i
!
k
!
∗
(
i
−
k
)
!
=
k
!
(
n
∗
n
!
(
n
−
k
)
!
−
∑
i
=
k
i
≤
N
c
n
t
i
∗
i
!
(
i
−
k
)
!
)
ans_k=n*\frac{n!} {k!*(n-k)!}-\sum_{i=k}^{i\leq N}cnt_i*\frac {i!} {k!*(i-k)!}=k!(n*\frac{n!} {(n-k)!}-\sum_{i=k}^{i\leq N}cnt_i*\frac {i!} {(i-k)!})
ansk=n∗k!∗(n−k)!n!−∑i=ki≤Ncnti∗k!∗(i−k)!i!=k!(n∗(n−k)!n!−∑i=ki≤Ncnti∗(i−k)!i!)
我们可以巧妙地把
N
N
N和
c
n
t
cnt
cnt以及其系数相同的部分合并一下:
f
i
=
{
−
c
n
t
i
∗
(
i
!
)
(
i
≠
n
)
n
∗
(
n
!
)
(
i
=
n
)
f_i=\begin{cases} -cnt_i*(i!)(i≠n)\\ n*(n!)(i=n)\\ \end{cases}
fi={−cnti∗(i!)(i̸=n)n∗(n!)(i=n)
把剩余部分再合并一下:
g
i
=
1
i
!
g_i=\frac {1} {i!}
gi=i!1
现在答案变为:
a
n
s
k
=
k
!
∗
(
∑
i
−
j
=
k
f
i
∗
g
j
)
ans_k=k!*(\sum_{i-j=k}f_i*g_j)
ansk=k!∗(∑i−j=kfi∗gj)
发现这玩意离卷积就差一步了。。。
然后随便翻转一个,接着用ntt算卷积就行了
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 524388
#define MOD 924844033
using namespace std;
typedef long long ll;
struct node{
int x;
node *nxt;
}edge[MAXN*2];
node *head[MAXN],*ncnt=edge;
void add_edge(int x,int y){
ncnt++;
ncnt->x=y;
ncnt->nxt=head[x];
head[x]=ncnt;
}
ll f[MAXN],g[MAXN];
ll G=5;
int u,v;
ll fsp(ll x,int y){
ll res=1;
while(y){
if(y&1)
res=res*x%MOD;
x=x*x%MOD;
y>>=1;
}
return res;
}
void ntt(ll a[],int N,int flag){
int i,j,k;
for(i=1,j=0;i<N;i++){
for(int d=N;j^=d>>=1,~j&d;);
if(i<j)
swap(a[i],a[j]);
}
for(i=1;i<N;i<<=1){
ll wn=fsp(G,(MOD-1)/(i<<1));
if(flag==0)
wn=fsp(wn,MOD-2);
for(j=0;j<N;j+=i<<1){
ll w=1;
for(k=0;k<i;k++,w=w*wn%MOD){
ll x=a[j+k],y=w*a[i+j+k]%MOD;
a[j+k]=(x+y)%MOD;
a[i+j+k]=(x-y+MOD)%MOD;
}
}
}
if(flag==0){
ll inv=fsp(N,MOD-2);
for(i=0;i<N;i++)
a[i]=a[i]*inv%MOD;
}
}
int siz[MAXN];
int cnt[MAXN];
int n;
void dfs(int x,int f){
siz[x]=1;
for(node *v=head[x];v!=NULL;v=v->nxt){
int u=v->x;
if(u==f)
continue;
dfs(u,x);
cnt[siz[u]]++;
cnt[n-siz[u]]++;
siz[x]+=siz[u];
}
}
ll fac[MAXN],ifac[MAXN];
int main(){
SF("%d",&n);
for(int i=1;i<n;i++){
SF("%d%d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs(1,0);
for(int i=1;i<n;i++)
cnt[i]=MOD-cnt[i];
cnt[n]=n;
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%MOD;
ifac[n]=fsp(fac[n],MOD-2);
for(int i=n;i>=1;i--)
ifac[i-1]=ifac[i]*i%MOD;
for(int i=1;i<=n;i++)
f[n-i]=cnt[i]*fac[i]%MOD;
for(int i=0;i<=n;i++)
g[i]=ifac[i];
int len=1;
while(len<=n*2)
len<<=1;
/*for(int i=0;i<n;i++){
ll res=0;
for(int j=0;j<=i;j++)
res=(res+f[j]*g[i-j])%MOD;
}*/
ntt(g,len,1);
ntt(f,len,1);
for(int i=0;i<len;i++)
g[i]=g[i]*f[i]%MOD;
ntt(g,len,0);
for(int i=1;i<=n;i++)
PF("%lld\n",g[n-i]*ifac[i]%MOD);
}