bzoj2159: Crash 的文明世界(树形dp+第二类斯特林数)

该博客介绍了一种利用树形动态规划(DP)和第二类斯特林数解决给定树结构中节点间距离求和问题的方法。通过转化公式和组合恒等式,将问题转化为树形DP状态转移,从而有效计算每个节点到其余节点距离的组合数之和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
题意:
给一棵树,边权为 1,对于每个点 i,输出 ∑ j = 1 n d i s ( i , j ) m , n ≤ 50000 , m ≤ 500 ∑_{j=1}^ndis(i, j)^m,n ≤ 50000, m ≤ 500 j=1ndis(i,j)m,n50000,m500
思路:
考虑对所有的 i ∈ [ 0 , m ] i\in[0,m] i[0,m]维护 ∑ d i s i ‾ \sum dis^{\underline i} disi
然后要用到一个下降幂之间的转化:
( x + 1 ) i ‾ = x i ‾ + i x i − 1 ‾ (x+1)^{\underline i}=x^{\underline i}+ix^{\underline{i-1}} (x+1)i=xi+ixi1
于是对于一个点 p p p
A n s p = ∑ v = 1 n d i s p , v m Ans_p=\sum_{v=1}^ndis_{p,v}^m Ansp=v=1ndisp,vm
= ∑ v = 1 n ∑ i = 0 m S m i d i s p , v i ‾ =\sum_{v=1}^n\sum_{i=0}^mS_{m}^idis_{p,v}^{\underline i} =v=1ni=0mSmidisp,vi
= ∑ i = 0 m i ! S m i ∑ v = 1 n C d i s p , v i =\sum_{i=0}^mi!S_{m}^i\sum_{v=1}^nC_{dis_{p,v}}^{i} =i=0mi!Smiv=1nCdisp,vi
然后发现我们学过组合恒等式 C d i s p , v i = C d i s p , v − 1 i + C d i s p , v − 1 i − 1 C_{dis_{p,v}}^{i}=C_{dis_{p,v}-1}^{i}+C_{dis_{p,v}-1}^{i-1} Cdisp,vi=Cdisp,v1i+Cdisp,v1i1
于是就可以树形 d p dp dp啦。
维护两个数组 f , g f,g f,g
f p , i f_{p,i} fp,i表示 p p p子树里所有点到自己的距离对应的 C d i s p , v i C_{dis_{p,v}}^{i} Cdisp,vi之和。
g p , i g_{p,i} gp,i表示 p p p子树外所有点到自己的距离对应的 C d i s p , v i C_{dis_{p,v}}^{i} Cdisp,vi之和。
然后随便转一转就能转出来。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
    static char buf[rlen],*ib,*ob;
    (ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
    return ib==ob?-1:*ib++;
}
inline int read(){
    int ans=0;
    char ch=gc();
    while(!isdigit(ch))ch=gc();
    while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
    return ans;
}
typedef long long ll;
const int mod=10007;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
const int N=50005,K=155;
vector<int>e[N];
int n,k,f[N][K],g[N][K],fac[K],S[K][K];
void dfs1(int p,int fa){
    f[p][0]=1;
    for(ri i=0,v;i<e[p].size();++i){
        if((v=e[p][i])==fa)continue;
        dfs1(v,p);
        for(ri j=0;j<=k;++j){
            Add(f[p][j],f[v][j]);
            if(j)Add(f[p][j],f[v][j-1]);
        }
    }
}
void dfs2(int p,int fa){
    for(ri i=0,v;i<e[p].size();++i){
        if((v=e[p][i])==fa)continue;
        for(ri j=0;j<=k;++j){
            Add(g[v][j],add(g[p][j],f[p][j]));
            if(j)Add(g[v][j],add(g[p][j-1],f[p][j-1]));
            Dec(g[v][j],f[v][j]);
            if(j)Dec(g[v][j],add(f[v][j-1],f[v][j-1]));
            if(j>1)Dec(g[v][j],f[v][j-2]);
        }
        dfs2(v,p);
    }
}
inline void init(int up){
    fac[0]=fac[1]=1;
    for(ri i=2;i<=up;++i)fac[i]=mul(i,fac[i-1]);
    S[0][0]=1;
    for(ri i=1;i<=up;++i)for(ri j=1;j<=i;++j)S[i][j]=add(S[i-1][j-1],mul(S[i-1][j],j));
}
int main(){
    n=read(),k=read();
    init(k);
    int L=read(),now=read(),A=read(),B=read(),Q=read();
    for(ri t,i=1,u,v;i<n;++i){
        now=(now*A+B)%Q;
        t=i<L?i:L;
        u=i-now%t,v=i+1;
        e[u].push_back(v),e[v].push_back(u);
    }
    dfs1(1,0),dfs2(1,0);
    for(ri t=0,p=1;p<=n;++p,t=0){
        for(ri i=0;i<=k;++i)Add(t,mul(add(f[p][i],g[p][i]),mul(fac[i],S[k][i])));
        cout<<t<<'\n';
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值