bzoj 2159: Crash 的文明世界 第二类斯特林数+树形dp

44 篇文章 0 订阅

题目大意:
给你一棵 n n n个点的树,边权都为1。
对于每一个点 i i i s ( i ) = ∑ j = 1 n d i s ( i , j ) k s(i)=\sum_{j=1}^{n}dis(i,j)^k s(i)=j=1ndis(i,j)k
其中 k k k给定常数,求所有点的s值。
n ≤ 50000 , k ≤ 150 n≤50000,k≤150 n50000k150

分析:
根据斯特林数的一个性质,
m n = ∑ i = 1 m S ( n , i ) ∗ P ( m , i ) m^n=\sum_{i=1}^{m}S(n,i)*P(m,i) mn=i=1mS(n,i)P(m,i)
其中, S S S表示第二类斯特林数, P P P表示排列数。
证明:考虑把 n n n个有区别的小球放入 m m m个有区别的盒子,求方案数。
n n n个小球放入 i i i个无区别的盒子,且不为空的方案数为 S ( n , i ) S(n,i) S(n,i)
我们枚举有小球的盒子数 i i i,再从 m m m个盒子中选出 i i i个。因为盒子有区别,所以乘排列数。
即方案为 ∑ i = 1 m S ( n , i ) ∗ P ( m , i ) \sum_{i=1}^{m}S(n,i)*P(m,i) i=1mS(n,i)P(m,i)
显然每个小球有 m m m种选择,方案数表示为 m n m^n mn
所以上式成立。
题目中的 s s s可以表示成,
s ( i ) = ∑ j = 1 n ∑ k = 1 m S ( m , k ) ∗ P ( d i s ( i , j ) , k ) s(i)=\sum_{j=1}^{n}\sum_{k=1}^{m}S(m,k)*P(dis(i,j),k) s(i)=j=1nk=1mS(m,k)P(dis(i,j),k)
= ∑ k = 1 m S ( m , k ) ∑ j = 1 n P ( d i s ( i , j ) , k ) =\sum_{k=1}^{m}S(m,k)\sum_{j=1}^{n}P(dis(i,j),k) =k=1mS(m,k)j=1nP(dis(i,j),k)
= ∑ k = 1 m S ( m , k ) ∗ k ! ∑ j = 1 n C ( d i s ( i , j ) , k ) =\sum_{k=1}^{m}S(m,k)*k!\sum_{j=1}^{n}C(dis(i,j),k) =k=1mS(m,k)k!j=1nC(dis(i,j),k)

关键求 T ( i ) = ∑ j = 1 n C ( d i s ( i , j ) , k ) T(i)=\sum_{j=1}^{n}C(dis(i,j),k) T(i)=j=1nC(dis(i,j),k)
f [ i ] [ j ] f[i][j] f[i][j]表示 i i i的子树内的点到 i i i,当 k = j k=j k=j T T T值和; g [ i ] [ j ] g[i][j] g[i][j]表示 i i i的子树外的点到 i i i,当 k = j k=j k=j T T T值和。
对于 f f f,考虑 x x x的一个儿子 y y y,因为这条边长为1,所以子树内所有的 d i s dis dis都要加1。
f [ x ] [ j ] + = f [ y ] [ j ] + f [ y ] [ j − 1 ] f[x][j]+=f[y][j]+f[y][j-1] f[x][j]+=f[y][j]+f[y][j1]
对于 g g g,首先他的父亲的所有点 d i s dis dis加1,即
g [ x ] [ j ] + = g [ f a ] [ j ] + g [ f a ] [ j − 1 ] g[x][j]+=g[fa][j]+g[fa][j-1] g[x][j]+=g[fa][j]+g[fa][j1]
他的兄弟到他的距离也要加1,可以看出整棵fa的子树减去当前子树,即
g [ x ] [ j ] + = f [ f a ] [ j ] + f [ f a ] [ j − 1 ] g[x][j]+=f[fa][j]+f[fa][j-1] g[x][j]+=f[fa][j]+f[fa][j1]
f [ f a ] [ j ] f[fa][j] f[fa][j] f [ f a ] [ j − 1 ] f[fa][j-1] f[fa][j1]都加多了,分别减去。
g [ x ] [ j ] − = f [ x ] [ j ] + f [ x ] [ j − 1 ] g[x][j]-=f[x][j]+f[x][j-1] g[x][j]=f[x][j]+f[x][j1]
g [ x ] [ j ] − = f [ x ] [ j − 1 ] + f [ x ] [ j − 2 ] g[x][j]-=f[x][j-1]+f[x][j-2] g[x][j]=f[x][j1]+f[x][j2]

代码:

/**************************************************************
    Problem: 2159
    User: ypxrain
    Language: C++
    Result: Accepted
    Time:4792 ms
    Memory:65584 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <cmath>
 
const int maxn=5e4+7;
const int mod=10007;
 
using namespace std;
 
int n,m,x,y,cnt,ans;
int L,now,A,B,Q;
int f[maxn][157],g[maxn][157],ls[maxn],s[157][157],jc[157];
 
struct edge{
    int y,next;
}e[maxn*2];
 
void add(int x,int y)
{
    e[++cnt]=(edge){y,ls[x]};
    ls[x]=cnt;
}
 
void dfs1(int x,int fa)
{
    f[x][0]=1;
    for (int i=ls[x];i>0;i=e[i].next)
    {
        int y=e[i].y;
        if (y==fa) continue;
        dfs1(y,x);
        f[x][0]=(f[x][0]+f[y][0])%mod;
        for (int j=1;j<=m;j++) f[x][j]=(f[x][j]+f[y][j]+f[y][j-1])%mod;
    }
}
 
void dfs2(int x,int fa)
{
    if (fa)
    {
        g[x][0]=n-f[x][0];
        for (int j=1;j<=m;j++)
        {
            g[x][j]=(g[x][j]+g[fa][j]+g[fa][j-1])%mod;
            g[x][j]=(g[x][j]+f[fa][j]+f[fa][j-1])%mod;
            g[x][j]=(g[x][j]+2*mod-f[x][j]-f[x][j-1])%mod;
            if (j>1) g[x][j]=(g[x][j]+2*mod-f[x][j-1]-f[x][j-2])%mod;
                else g[x][j]=(g[x][j]+mod-f[x][j-1])%mod;
        }
    }
    for (int i=ls[x];i>0;i=e[i].next)
    {
        int y=e[i].y;
        if (y==fa) continue;
        dfs2(y,x);
    }
}
 
int main()
{
    scanf("%d%d%d%d%d%d%d",&n,&m,&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;
        add(x,y);
        add(y,x);
    }
    s[0][0]=jc[0]=1;
    for (int i=1;i<=m;i++)
    {
        jc[i]=jc[i-1]*i%mod;
        for (int j=1;j<=i;j++) s[i][j]=(s[i-1][j]*j%mod+s[i-1][j-1])%mod;
    }
    dfs1(1,0);   
    dfs2(1,0);  
    for (int i=1;i<=n;i++)
    {
        int ans=0;
        for (int j=1;j<=m;j++) ans=(ans+s[m][j]*jc[j]%mod*(f[i][j]+g[i][j])%mod)%mod;
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值