[BZOJ2159][洛谷P4827][国家集训队] Crash 的文明世界

【题目大意】
给定一棵 n n n个点的树和正整数 k k k,每条边长度都为 1 1 1,对于每个点 u u u求: ∑ j = 1 n d i s t ( u , j ) k ; \sum_{j=1}^{n}dist(u,j)^k; j=1ndist(u,j)k; n &lt; = 50000 , k &lt; = 150 n&lt;=50000,k&lt;=150 n<=50000,k<=150,答案对 10007 10007 10007取模

【算法分析】

组合数学+树形 d p dp dp

  • 先给出一个式子:
  • a k = ∑ i = 1 k S ( k , i ) ∗ i ! ∗ C ( a , i ) a^k=\sum_{i=1}^{k}S(k,i)*i!*C(a,i) ak=i=1kS(k,i)i!C(a,i)
  • 解释: a k a^k ak看作 k k k个球放入 a a a个有区别的盒子里的方案数
  • S ( k , i ) S(k,i) S(k,i)为第二类斯特林数,表示 k k k个球放入 i i i个盒子,不允许盒子空着,且这 i i i个盒子无区别的方案数
  • i ! ∗ C ( a , i ) i!*C(a,i) i!C(a,i)其实就是 A ( a , i ) A(a,i) A(a,i)
  • 上式即枚举 i i i个盒子非空,用 S ( k , i ) S(k,i) S(k,i)乘上a个盒子中,有序选i个的方案数
  • 因此原式 = ∑ j = 1 n ∑ i = 1 k S ( k , i ) ∗ i ! ∗ C ( d i s t ( u , j ) , i ) =\sum_{j=1}^{n}\sum_{i=1}^{k}S(k,i)*i!*C(dist(u,j),i) =j=1ni=1kS(k,i)i!C(dist(u,j),i)
  • 考虑先枚举 i i i,那么原式化为 ∑ i = 1 k S ( k , i ) ∗ i ! ∗ ∑ j = 1 n C ( d i s t ( u , j ) , i ) \sum_{i=1}^{k}S(k,i)*i!*\sum_{j=1}^{n}C(dist(u,j),i) i=1kS(k,i)i!j=1nC(dist(u,j),i)
  • 其中 S ( k , i ) = S ( k − 1 , i ) ∗ i + S ( k − 1 , i − 1 ) S(k,i)=S(k-1,i)*i+S(k-1,i-1) S(k,i)=S(k1,i)i+S(k1,i1)
  • 问题转化为求 ∑ j = 1 n C ( d i s t ( u , j ) , i ) \sum_{j=1}^{n}C(dist(u,j),i) j=1nC(dist(u,j),i)
  • 考虑树形 d p dp dp,即将 j j j按是否在 u u u子树内分类
  • f [ u ] [ i ] f[u][i] f[u][i]表示 ∑ j 在 u 子 树 内 C ( d i s t ( u , j ) , i ) \sum_{j在u子树内}C(dist(u,j),i) juC(dist(u,j),i)
  • 众所周知 C ( x , y ) = C ( x − 1 , y − 1 ) + C ( x − 1 , y ) C(x,y)=C(x-1,y-1)+C(x-1,y) C(x,y)=C(x1,y1)+C(x1,y)
  • 于是枚举 u u u的每个儿子 v v v进行递推:
    f [ u ] [ i ] + = ∑ j 在 v 子 树 内 C ( d i s t ( v , j ) , i ) + C ( d i s t ( v , j ) , i − 1 ) f[u][i]+=\sum_{j在v子树内}C(dist(v,j),i)+C(dist(v,j),i-1) f[u][i]+=jvC(dist(v,j),i)+C(dist(v,j),i1)
  • f [ u ] [ i ] + = f [ v ] [ i ] + f [ v ] [ i − 1 ] f[u][i]+=f[v][i]+f[v][i-1] f[u][i]+=f[v][i]+f[v][i1]
  • 注意特判 f [ u ] [ 0 ] + = f [ v ] [ 0 ] f[u][0]+=f[v][0] f[u][0]+=f[v][0],即 i = 0 i=0 i=0的时候不要 + = f [ v ] [ i − 1 ] +=f[v][i-1] +=f[v][i1]
  • g [ u ] [ i ] g[u][i] g[u][i]表示 ∑ j = 1 n C ( d i s t ( u , j ) , i ) \sum_{j=1}^{n}C(dist(u,j),i) j=1nC(dist(u,j),i)
  • 枚举 u u u的每个儿子 v v v
  • 能给 g [ v ] [ i ] g[v][i] g[v][i]贡献的部分即 ∑ j 不 在 v 子 树 中 C ( d i s t ( u , j ) , i ) \sum_{j不在v子树中}C(dist(u,j),i) jvC(dist(u,j),i) ,记为 n o w [ i ] now[i] now[i]
  • 显然 n o w [ i ] = g [ u ] [ i ] − f [ v ] [ i − 1 ] − f [ v ] [ i ] now[i]=g[u][i]-f[v][i-1]-f[v][i] now[i]=g[u][i]f[v][i1]f[v][i]
  • 再递推到 v v v g [ v ] [ i ] = f [ v ] [ i ] + n o w [ i ] + n o w [ i − 1 ] g[v][i]=f[v][i]+now[i]+now[i-1] g[v][i]=f[v][i]+now[i]+now[i1]
  • 与上文 f [ u ] [ 0 ] f[u][0] f[u][0]同理,注意特判 g [ v ] [ 0 ] g[v][0] g[v][0]

【参考程序】

#include <bits/stdc++.h>

using namespace std;

template <class t>
inline void read(t & res)
{
   char ch;
   while (ch = getchar(), !isdigit(ch));
   res = ch ^ 48;
   while (ch = getchar(), isdigit(ch))
   res = res * 10 + (ch ^ 48);
}

const int e = 50005, o = 155, mod = 10007;
int n, m, f[e][o], g[e][o], now[o], s[o][o], adj[e], nxt[e * 2], go[e * 2], num, fac[o];

inline void link(int x, int y)
{
   nxt[++num] = adj[x];
   adj[x] = num;
   go[num] = y;
   nxt[++num] = adj[y];
   adj[y] = num;
   go[num] = x; 
}

inline void add(int &x, int y)
{
   x += y;
   while (x >= mod) x -= mod;
}

inline void dfs1(int u, int pa)
{
   int i, j;
   f[u][0] = 1;
   for (i = adj[u]; i; i = nxt[i])
   {
   	int v = go[i];
   	if (v == pa) continue;
   	dfs1(v, u);
   	add(f[u][0], f[v][0]);
   	for (j = 1; j <= m; j++) add(f[u][j], f[v][j - 1] + f[v][j]);
   }
   for (j = 0; j <= m; j++) g[u][j] = f[u][j];
}

inline void dfs2(int u, int pa)
{
   int i, j;
   for (i = adj[u]; i; i = nxt[i])
   {
   	int v = go[i];
   	if (v == pa) continue;
   	for (j = 0; j <= m; j++) now[j] = g[u][j];
   	add(now[0], mod - f[v][0]);
   	for (j = 1; j <= m; j++) add(now[j], 2 * mod - f[v][j] - f[v][j - 1]);
   	add(g[v][0], now[0]);
   	for (j = 1; j <= m; j++) add(g[v][j], now[j - 1] + now[j]);
   	dfs2(v, u);
   }
}

int main()
{
   int i, j, x, y;
   read(n); read(m);
   for (i = 1; i < n; i++)
   {
   	read(x);
   	read(y);
   	link(x, y);
   }
   fac[0] = 1;
   for (i = 1; i <= m; i++) fac[i] = fac[i - 1] * i % mod;
   for (i = 1; i <= m; i++)
   for (j = 1; j <= i; j++)
   if (j == 1) s[i][j] = 1;
   else s[i][j] = (s[i - 1][j] * j + s[i - 1][j - 1]) % mod;
   dfs1(1, 0);
   dfs2(1, 0);
   for (i = 1; i <= n; i++)
   {
   	int ans = 0;
   	for (j = 1; j <= m; j++) add(ans, 1ll * s[m][j] * fac[j] * g[i][j] % mod);
   	printf("%d\n", ans);
   }
   return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值