BZOJ2159 Crash的文明世界(树形dp+斯特林数)
根据组合意义,有nk=ΣC(n,i)*i!*S(k,i) (i=0~k),即将k个有标号球放进n个有标号盒子的方案数=在n个盒子中选i个将k个有标号球放入并且每个盒子至少有一个球。
回到本题,可以令f[i][j]表示ΣC(dis(i,k),j) (k为i子树中节点),通过C(i,j)=C(i-1,j)+C(i-1,j-1)转移。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 50010 #define K 155 #define P 10007 int n,m,l,now,A,B,Q,tmp,p[N],t=0; int f[N][K],S[K][K],fac[K],ans[N]; struct data{int to,nxt; }edge[N<<1]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} void dfs(int k,int from) { f[k][0]=1; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) { dfs(edge[i].to,k); f[k][0]=(f[k][0]+f[edge[i].to][0])%P; for (int j=1;j<=m;j++) f[k][j]=(f[k][j]+f[edge[i].to][j]+f[edge[i].to][j-1])%P; } } void getans(int k,int from) { for (int j=0;j<=m;j++) ans[k]=(ans[k]+f[k][j]*fac[j]%P*S[m][j])%P; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) { for (int j=m;j>=2;j--) f[edge[i].to][j]=((f[k][j]-f[edge[i].to][j-1]+f[k][j-1]-f[edge[i].to][j-1]-f[edge[i].to][j-2])%P+P)%P; f[edge[i].to][1]=((f[k][1]-f[edge[i].to][0]+f[k][0]-f[edge[i].to][0])%P+P)%P; f[edge[i].to][0]=n; getans(edge[i].to,k); } } int main() { #ifndef ONLINE_JUDGE freopen("bzoj2159.in","r",stdin); freopen("bzoj2159.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(),l=read(),now=read(),A=read(),B=read(),Q=read(); for (int i=1;i<n;i++) { now=(now*A+B)%Q; tmp=(i<l)?i:l; int x=i-now%tmp,y=i+1; addedge(x,y),addedge(y,x); } dfs(1,1); fac[0]=1;for (int i=1;i<=m;i++) fac[i]=fac[i-1]*i%P; S[0][0]=1; for (int i=1;i<=m;i++) for (int j=1;j<=i;j++) S[i][j]=(S[i-1][j-1]+S[i-1][j]*j)%P; getans(1,1); for (int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }