一道组合数学的好题。
首先是
n
k
2
nk^2
nk2的
50
50
50分暴力:
从
x
k
−
>
(
x
+
1
)
k
x^k->(x+1)^k
xk−>(x+1)k,用二项式定理展开每个节点维护
k
+
1
k+1
k+1个数
两次树形
d
p
dp
dp分别求子树的和从父亲上传下来的,
f
[
u
]
[
j
]
f[u][j]
f[u][j]表示
u
u
u节点子树到它的
j
j
j次幂和。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 50005
using namespace std;
const int mod=10007;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int n,k,cnt,head[N],f[N][151],g[N][151],h[N][151],C[151][151];
struct EDGE{
int to,nxt;
}edge[N<<1];
inline void add(int x,int y){
edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}
inline void prework(){
for(int i=0;i<=k;i++) C[i][0]=1;
for(int i=1;i<=k;i++)
for(int j=1;j<=k;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
void dfs1(int u,int fa){
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa) continue;
dfs1(v,u);
for(int j=0;j<=k;j++){
for(int l=0;l<=j;l++)
(h[v][j]+=1LL*f[v][l]*C[j][j-l]%mod)%=mod;
(f[u][j]+=h[v][j])%=mod;
}
for(int j=0;j<=k;j++) (++f[u][j])%=mod,(++h[v][j])%=mod;
} return;
}
void dfs2(int u,int fa){
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa) continue;
for(int j=0;j<=k;j++){
for(int l=0;l<=j;l++)
(g[v][j]+=1LL*g[u][l]*C[j][j-l]%mod)%=mod;
for(int l=0;l<=j;l++)
(g[v][j]+=1LL*(f[u][l]-h[v][l]+mod)%mod*C[j][j-l]%mod)%=mod;
}
for(int j=0;j<=k;j++) (++g[v][j])%=mod;
dfs2(v,u);
} return;
}
int main(){
n=rd(); k=rd();
for(int i=1;i<n;i++){
int x=rd(),y=rd();
add(x,y); add(y,x);
}
prework();
dfs1(1,1); dfs2(1,1);
for(int i=1;i<=n;i++) printf("%d\n",(f[i][k]+g[i][k])%mod);
return 0;
}
关于求 k k k次幂的计算,可以转化成求组合数
首先有一个公式:
n
k
=
∑
i
=
0
k
S
(
k
,
i
)
×
i
!
×
C
(
n
,
i
)
n^k=\sum_{i=0}^k S(k,i)\times i!\times C(n,i)
nk=∑i=0kS(k,i)×i!×C(n,i)
其中
S
S
S表示第二类
s
t
r
i
l
i
n
g
striling
striling数,有递推公式:
S
(
n
,
i
)
=
S
(
n
−
1
,
i
)
×
i
+
S
(
n
−
1
,
i
−
1
)
S(n,i)=S(n-1,i)\times i+S(n-1,i-1)
S(n,i)=S(n−1,i)×i+S(n−1,i−1)
这样的话
S
S
S和
i
!
i!
i!是不变的都可以预处理出来,然后只需要考虑组合数的变化,因为
C
(
n
,
i
)
=
C
(
n
−
1
,
i
−
1
)
+
C
(
n
−
1
,
i
)
C(n,i)=C(n-1,i-1)+C(n-1,i)
C(n,i)=C(n−1,i−1)+C(n−1,i),所以可以维护
f
[
u
]
[
j
]
f[u][j]
f[u][j]表示子树中
∑
k
C
(
d
i
s
t
(
u
,
k
)
,
j
)
\sum_k C(dist(u,k),j)
∑kC(dist(u,k),j),父亲上面的同理,然后树形
d
p
dp
dp维护
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 50005
using namespace std;
const int mod=10007;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
int n,k,cnt,head[N],f[N][151],g[N][151],s[151][151],C[N][151],fac[151];
struct EDGE{
int to,nxt;
}edge[N<<1];
inline void add(int x,int y){
edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}
inline void prework(){
for(int i=0;i<=k;i++) C[i][0]=1,s[i][i]=1; s[0][0]=0;
for(int i=1;i<=N;i++)
for(int j=1;j<=k;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
for(int i=2;i<=k;i++)
for(int j=1;j<=i;j++) s[i][j]=(s[i-1][j]*j%mod+s[i-1][j-1])%mod;
fac[0]=1;
for(int i=1;i<=k;i++) fac[i]=1LL*fac[i-1]*i%mod;
}
void dfs1(int u,int fa){
f[u][0]=1;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa) continue;
dfs1(v,u); (f[u][0]+=f[v][0])%=mod;
for(int j=1;j<=k;j++){
(f[u][j]+=(f[v][j]+f[v][j-1])%mod)%=mod;
}
} return;
}
void dfs2(int u,int fa){
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].to;
if(v==fa) continue;
g[v][0]=n-f[v][0];
for(int j=1;j<=k;j++){
g[v][j]=((g[u][j-1]+g[u][j]+f[u][j-1]+f[u][j]-2*f[v][j-1]-f[v][j])%mod+mod)%mod;
if(j>1) g[v][j]=(g[v][j]-f[v][j-2]+mod)%mod;
}
dfs2(v,u);
} return;
}
int main(){
n=rd(); k=rd();
for(int i=1;i<n;i++){
int x=rd(),y=rd();
add(x,y); add(y,x);
}
prework();
dfs1(1,1); dfs2(1,1);
for(int i=1;i<=n;i++){
int ans=0;
for(int j=0;j<=k;j++)
(ans+=1LL*s[k][j]*fac[j]%mod*(f[i][j]+g[i][j])%mod)%=mod;
printf("%d\n",ans);
}
return 0;
}