[BZOJ2159]Crash的文明世界 树型DP+第二类Striling数

听说形如 xk 的都是Stirling数的套路?我怎么没听说过啊。。。
有个性质:

xk=i=1kS(k,i)i!(xi)

考虑一个组合意义证明,就是给 k 个格子染 x 种色的方案数,等于先把这 k 个格子分成若干集合,在选出相同个数的颜色,每个集合染同一种颜色的方案数。 i!(xi) 这部分好像也可写成下降幂。
然后因为组合数的杨辉三角性质 (nm)=(n1m1)+(n1m) ,这题边权又恰好为 1 ,所以很好DP。
di,j=(dist(i,x)j) j i 的子树中, ui,j 则表示不在子树中的。那么方程如下:
di,j=dson,j+dson,j1
ui,j=ufa,j+ufa,j1+dfa,j+dfa,j1di,j2di,j1di,j2
减掉的那些东西就是去掉其父亲中自己的贡献。
代码:

#include
#include
#define up(x,y) x=((x)+(y))%mod
using namespace std;
const int maxn=50010;
const int mod=10007;
int n,K,u[maxn][155],d[maxn][155],S[155][155],ans[maxn],jie[155];
struct edge
{
    int t;
    edge *next;
}*con[maxn];
void ins(int x,int y)
{
    edge *p=new edge;
    p->t=y;
    p->next=con[x];
    con[x]=p;
}
void dfs1(int v,int fa)
{
    d[v][0]=1;
    for(edge *p=con[v];p;p=p->next)
        if(p->t!=fa) 
        {
            dfs1(p->t,v);
            up(d[v][0],d[p->t][0]);
            for(int i=1;i<=K;i++)
                up(d[v][i],d[p->t][i]+d[p->t][i-1]);
        }
}
void dfs2(int v,int fa)
{
    u[v][0]=n-d[v][0];
    if(fa)for(int i=1;i<=K;i++)
        up(u[v][i],u[fa][i]+u[fa][i-1]+d[fa][i]+d[fa][i-1]-d[v][i]-2*d[v][i-1]-(i==1?0:d[v][i-2])+(mod<<2));
    for(edge *p=con[v];p;p=p->next)
        if(p->t!=fa) dfs2(p->t,v);    
}
int main()
{
    int now,A,B,Q,L;  
    scanf("%d%d",&n,&K);  
    scanf("%d%d%d%d%d",&L,&now,&A,&B,&Q);  
    for(int i=1,tot=0;i*A+B)%Q;  
        int x=i-now%((iy=i+1;  
        ins(x,y);ins(y,x);
    }
    S[1][1]=1;
    for(int i=2;i<=K;i++)
        for(int j=1;j<=K;j++)
            S[i][j]=(S[i-1][j-1]+S[i-1][j]*j)%mod;
    dfs1(1,0);dfs2(1,0);
    jie[0]=1;
    for(int i=1;i<=K;i++)
        jie[i]=jie[i-1]*i%mod; 
    for(int i=1;i<=n;i++)
        for(int j=1;j<=K;j++)
            up(ans[i],S[K][j]*jie[j]%mod*(u[i][j]+d[i][j]));
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[i]);              
    return 0;
}

你可能感兴趣的:(dp,树,组合数学,Stirling数)