zoukankan      html  css  js  c++  java
  • 【洛谷4827】[国家集训队] Crash 的文明世界(斯特林数+换根DP)

    点此看题面

    • 给定一棵(n)个点的树,对于每个点(i),求出(sum_{j=1}^ndist(i,j)^k)
    • (nle5 imes10^4,kle150)

    第二类斯特林数与自然数幂和

    可详见这篇博客:斯特林数的基础性质与斯特林反演的初步入门

    这里只给出结论:

    [sum_{i=0}^n i^k=sum_{j=0}^kS_2(k,j)j!C_{n+1}^{j+1} ]

    考虑我们可以枚举(j),则(S_2(k,j)j!)都是已知的,只要对于每个(j)求出(C_{n+1}^{j+1})即可。

    换根(DP)

    考虑先强制(1)号点为根,那么只要求出每个点子树内的答案。

    如果当前点(x)与子树内一个点(y)距离为(dist(x,y)),那么(x)的子节点到(y)的距离就是(dist(x,y)-1)

    而众所周知(C_{dist(x,y)+1}^{j+1}=C_{dist(x,y)}^j+C_{dist(x,y)}^{j+1})

    我们用(f_{x,j})表示(x)子树内(C_{dist(x,y)+1}^{j+1})之和,那么根据上面这个式子就有转移:

    [f_{x,j}=sum(f_{son,j-1}+f_{son,j}) ]

    然后只要再(dfs)一遍做一次换根(DP)就好了。

    代码:(O(nk))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50000
    #define K 150
    #define X 10007
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,k,S2[K+5][K+5],ee,lnk[N+5];struct edge {int to,nxt;}e[N<<1];
    int f[N+5][K+5];I void dfs1(CI x,CI lst=0)//第一遍dfs以1为根DP
    {
    	f[x][0]=1;for(RI i=lnk[x],j;i;i=e[i].nxt) if(e[i].to^lst) for(dfs1(e[i].to,x),
    		j=0;j<=k;++j) f[x][j]=(0LL+f[x][j]+f[e[i].to][j]+(j?f[e[i].to][j-1]:0))%X;//DP转移
    }
    int g[K+5],ans[N+5];I void dfs2(CI x,CI lst=0)//第二遍dfs换根DP
    {
    	RI i,j,F=1;for(i=0;i<=k;F=1LL*F*(++i)%X) ans[x]=(1LL*S2[k][i]*F%X*f[x][i]+ans[x])%X;//计算以当前点为根的答案
    	for(i=lnk[x];i;i=e[i].nxt) if(e[i].to^lst)
    	{
    		for(j=0;j<=k;++j) g[j]=(2LL*X+f[x][j]-f[e[i].to][j]-(j?f[e[i].to][j-1]:0))%X;//消去该子节点贡献
    		for(j=0;j<=k;++j) f[e[i].to][j]=(0LL+f[e[i].to][j]+g[j]+(j?g[j-1]:0))%X;dfs2(e[i].to,x);//更新子节点DP数组并继续DP
    	}
    }
    int main()
    {
    	RI i,j,x,y;for(scanf("%d%d",&n,&k),i=1;i^n;++i) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    	for(S2[0][0]=i=1;i<=k;++i) for(j=1;j<=i;++j) S2[i][j]=(1LL*j*S2[i-1][j]+S2[i-1][j-1])%X;//预处理第二类斯特林数
    	for(dfs1(1),dfs2(1),i=1;i<=n;++i) printf("%d
    ",ans[i]);return 0;
    }
    
  • 相关阅读:
    LINQ学习系列-----1.3 扩展方法
    表单重复提交的三种情况及解决办法
    JDBC的简单封装
    Java学习路线图
    成为一名Java高级工程师你需要学什么
    站在烦恼里仰望幸福
    如何发布Web项目到互联网
    用户管理的设计--2.新增用户信息实现
    MD5加密工具
    springMvc注解之@ResponseBody和@RequestBody
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4827.html
Copyright © 2011-2022 走看看