【题意】
给出一棵
n
个点的树,求对于每个点
d(i)=∑i≠x1≤x≤ndist(x,i)k
数据范围: 1≤n≤50000,1≤k≤150
【题解】
这题非常的神……
首先我们发现, xn 能用Stirling数通过一些神奇的方法表示出来……
xn=∑1<=k<=nS(n,k)×F(x,k)
其中 S(n,k) 为第二类Stirling数, S(n,k)=S(n−1,k−1)+k×S(n−1,k)
F(n,k)=n×(n−1)×...×(n−k+1)
那么 d(i)=∑j≤kS(k,j)×f(i,j)
其中 f(i,j)=∑x≠i1≤x≤nF(dist(i,x),j)
那么我们就能推出来啦。
但是我们发现,f数组很难通过树形dp求出。考虑到 C(n,k)=F(n,k)k! ,我们更改f数组表示的内容,改为
f′(i,j)=∑x≠i1≤x≤nC(dist(i,x),j) 。
那么我们就有 f(i,j)=f′(i,j)×j! ,我们可以通过pascal定理转移出f’,从而转移出f。
pascal定理: C(n,k)=C(n−1,k)+C(n−1,k−1)
然后我们就可以用树形dp统计啦。
注意对于一个节点,树形dp中的f表示的是其以下的子树的和,还需要统计它的兄弟以及父亲上面的值,需要重新计算。
记录down[x][j]表示
C(dist(i,x),j)
子树对x的贡献
记录up[x][j]表示
C(dist(i,x),j)
x的父亲和父亲的父亲…和兄弟的对x的贡献
down[x][j]+=down[son][j]+down[son][j−1]
up[x][j]+=up[fa][j]+up[fa[j−1]
up[x][j]+=down[fa][j]+down[fa][j−1]
up[x][j]−=down[x][j]+down[x][j−1]
up[x][j]−=down[x][j−1]+down[x][j−2](j≥2)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=10007;
const int N=100100;
const int M=200;
int n,k,L,A,B,Q,now;
int tot,ans,h[N];
int s[M][M],c[M][M],fac[M];
int down[N][M],up[N][M];
struct edge{int y,next;}g[N];
void adp(int x,int y){
g[++tot].y=y;
g[tot].next=h[x];
h[x]=tot;
}
void calcup(int x,int fa){
if (fa){
up[x][0]=n-down[x][0];
for (int j=1;j<=k;j++){
up[x][j]=(up[x][j]+up[fa][j]+up[fa][j-1])%mod;
up[x][j]=(up[x][j]+down[fa][j]+down[fa][j-1])%mod;
up[x][j]=(up[x][j]-down[x][j]-down[x][j-1]+2*mod)%mod;
up[x][j]=(up[x][j]-down[x][j-1]+mod)%mod;
if (j>=2) up[x][j]=(up[x][j]-down[x][j-2]+mod)%mod;
}
}
for (int i=h[x];i;i=g[i].next)
if (g[i].y!=fa)
calcup(g[i].y,x);
}
void calcdown(int x,int fa){
down[x][0]=1;
for (int i=h[x];i;i=g[i].next)
if (g[i].y!=fa){
calcdown(g[i].y,x);
down[x][0]+=down[g[i].y][0];
for (int j=1;j<=k;j++)
down[x][j]=(down[x][j]+down[g[i].y][j]+down[g[i].y][j-1])%mod;
}
}
int main(){
freopen("a.in","r",stdin);
scanf("%d%d%d%d%d%d%d",&n,&k,&L,&now,&A,&B,&Q);
for (int i=1;i<n;i++){
now=(now*A+B)%Q;
int tmp=i<L?i:L;
int x=i-now%tmp,y=i+1;
adp(x,y);adp(y,x);
}
s[0][0]=fac[0]=1;
for (int i=1;i<=k;i++){
fac[i]=fac[i-1]*i%mod;
s[i][i]=1;
for (int j=1;j<i;j++)
s[i][j]=(s[i-1][j]*j%mod+s[i-1][j-1])%mod;
}
calcdown(1,0);
calcup(1,0);
for (int i=1;i<=n;i++){
ans=0;
for (int j=1;j<=k;j++)
ans=(ans+s[k][j]*fac[j]%mod*(up[i][j]+down[i][j])%mod)%mod;
printf("%d\n",ans);
}
}