【BZOJ2159】Crash的文明世界 【斯特林数】【树形dp】

题目传送门
题意:求对于每个i的 nj=1dis(i,j)k ∑ j = 1 n d i s ( i , j ) k
这里有一个公式: xk=xi=1S(k,i)Aix x k = ∑ i = 1 x S ( k , i ) A x i 。S是第二类斯特林数,A是排列数。
其实这个式子的组合意义就是把 k k 个球放进x个盒子的方案数。右边的意思是枚举有哪些盒子要放球,选球的方案数乘上选盒子的方案数。
这个是不好统计的,我们把它变一下: xk=xi=1S(k,i)i!Cix x k = ∑ i = 1 x S ( k , i ) i ! C x i
于是我们令 f[i][j]=nk=1C(dis(i,k),j) f [ i ] [ j ] = ∑ k = 1 n C ( d i s ( i , k ) , j ) ,通过两次树形dp求出f数组,最后再统计一下即可。具体方法详见代码。

常数极大的代码

#include<cstdio>
typedef int ll;
const int N=50005,M=155;
const ll mod=10007;
int n,k,u,v,cnt,head[N],to[N*2],nxt[N*2];
ll ans,f[N][M][2],jc[M],s[M][M];
void adde(int u,int v){
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
void dfs1(int pre,int u){
    f[u][0][0]=1;
    int v;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        if(v!=pre){
            dfs1(u,v);
            f[u][0][0]=(f[u][0][0]+f[v][0][0])%mod;
            for(int j=1;j<=k;j++){
                f[u][j][0]=(f[u][j][0]+f[v][j-1][0]+f[v][j][0])%mod;
            }
        }
    }
}
void dfs2(int pre,int u){
    if(pre){
        f[u][0][1]=(n-f[u][0][0]+mod)%mod;
        for(int i=1;i<=k;i++){
            f[u][i][1]=(f[u][i][1]+f[pre][i][0]+f[pre][i-1][0]+f[pre][i-1][1]+f[pre][i][1])%mod;
            f[u][i][1]=((f[u][i][1]-f[u][i][0]-2*f[u][i-1][0])%mod+mod)%mod;
            if(i>1){
                f[u][i][1]=(f[u][i][1]-f[u][i-2][0]+mod)%mod;
            }
        }
    }
    int v;
    for(int i=head[u];i;i=nxt[i]){
        v=to[i];
        if(v!=pre){
            dfs2(u,v);
        }
    }
}
int main(){
    int L,now,A,B,Q,tmp;
    scanf("%d%d%d",&n,&k,&L); 
    scanf("%d%d%d%d",&now,&A,&B,&Q); 
    for (int i=1;i<n;i++){ 
        now=(now*A+B)%Q;
        tmp=(i<L)?i:L;
        u=i-now%tmp;
        v=i+1;
        adde(u,v);
        adde(v,u);
    }
/*  scanf("%d%d",&n,&k);
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        adde(u,v);
        adde(v,u);
    }*/
    dfs1(0,1);
    dfs2(0,1);
    jc[0]=s[0][0]=1;
    for(int i=1;i<=k;i++){
        jc[i]=i*jc[i-1]%mod;
        for(int j=1;j<=k;j++){
            s[i][j]=(s[i-1][j-1]+j*s[i-1][j])%mod;
        }
    }
    for(int i=1;i<=n;i++){
        ans=0;
        for(int j=1;j<=k;j++){
            ans=(ans+1LL*s[k][j]*jc[j]%mod*(f[i][j][0]+f[i][j][1])%mod)%mod;
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值