[BZOJ2159]Crash 的文明世界(第二类斯特林数 + 树形 DP)

Address

Solution

  • 好久没写博客了,来水一篇
  • 发现一个困难: d i s t ( i , j ) k dist(i,j)^k dist(i,j)k 没有可加性
  • 换句话说, ( d i s t ( i , j ) + 1 ) k (dist(i,j)+1)^k (dist(i,j)+1)k 不能用仅含 d i s t ( i , j ) k dist(i,j)^k dist(i,j)k 的式子表示出来
  • 使用二项式定理展开 ( d i s t ( i , j ) + 1 ) k (dist(i,j)+1)^k (dist(i,j)+1)k ,可以做到 O ( n k 2 ) O(nk^2) O(nk2) 的优秀复杂度,拿到 50 50 50 分的优秀得分
  • 考虑等式
  • i k = ∑ j { k j } × ( i j ) × j ! i^k=\sum_j\begin{Bmatrix}k\\j\end{Bmatrix}\times\binom ij\times j! ik=j{kj}×(ji)×j!
  • 证明右转 蒟蒻的博客
  • 其中 { n m } \begin{Bmatrix}n\\m\end{Bmatrix} {nm} 为第二类斯特林数,即 n n n 个元素划分成 m m m 个无序非空集合的方案数, ( n m ) \binom nm (mn) 为组合数 ,即 n n n 个元素中选出 m m m 个无序元素的方案数
  • 把式子化一下
  • ∑ j = 1 n d i s t ( i , j ) k = ∑ j = 1 n ∑ h = 0 k { k h } × ( d i s t ( i , j ) h ) × h ! \sum_{j=1}^ndist(i,j)^k=\sum_{j=1}^n\sum_{h=0}^k\begin{Bmatrix}k\\h\end{Bmatrix}\times\binom{dist(i,j)}h\times h! j=1ndist(i,j)k=j=1nh=0k{kh}×(hdist(i,j))×h!
  • = ∑ h = 0 k { k h } × h ! × ∑ j = 1 n ( d i s t ( i , j ) h ) =\sum_{h=0}^k\begin{Bmatrix}k\\h\end{Bmatrix}\times h!\times\sum_{j=1}^n\binom{dist(i,j)}h =h=0k{kh}×h!×j=1n(hdist(i,j))
  • 于是我们要求的就是对于每个 1 ≤ u ≤ n 1\le u\le n 1un 0 ≤ i ≤ k 0\le i\le k 0ik ,求
  • ∑ v = 1 n ( d i s t ( u , v ) i ) \sum_{v=1}^n\binom{dist(u,v)}i v=1n(idist(u,v))
  • 我们的状态出来了
  • f [ u ] [ i ] f[u][i] f[u][i] 表示 u u u u u u 的子树内的所有点的距离,的 ( d i s t i ) \binom{dist}i (idist) 之和
  • 根据优美的组合数公式 ( n m ) = ( n − 1 m ) + ( n − 1 m − 1 ) \binom nm=\binom{n-1}m+\binom{n-1}{m-1} (mn)=(mn1)+(m1n1) 得到
  • f [ u ] [ i ] = ∑ v ∈ s u b s t r e e [ u ] ( d i s t ( u , v ) i ) f[u][i]=\sum_{v\in substree[u]}\binom{dist(u,v)}i f[u][i]=vsubstree[u](idist(u,v))
  • = ∑ v ∈ s u b s t r e e [ u ] ( ( d i s t ( u , v ) − 1 i ) + ( d i s t ( u , v ) − 1 i − 1 ) ) =\sum_{v\in substree[u]}(\binom{dist(u,v)-1}i+\binom{dist(u,v)-1}{i-1}) =vsubstree[u]((idist(u,v)1)+(i1dist(u,v)1))
  • = [ i = 0 ] + ∑ v ∈ s o n [ u ] ( f [ v ] [ i ] + f [ v ] [ i − 1 ] ) =[i=0]+\sum_{v\in son[u]}(f[v][i]+f[v][i-1]) =[i=0]+vson[u](f[v][i]+f[v][i1])
  • 然而我们要求的不仅仅是源点为 1 1 1 的答案,故我们还需要换根
  • g [ u ] [ i ] g[u][i] g[u][i] 表示 u u u 到整棵树除了 u u u 的子树之外的所有点的距离,的 ( d i s t i ) \binom{dist}i (idist) 之和
  • 转移和 f f f 差不多,注意计算 g [ f a t h e r [ u ] ] g[father[u]] g[father[u]] 时需要消除 f [ u ] f[u] f[u] 的影响
  • 复杂度 O ( n k ) O(nk) O(nk) 非常优秀

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Tree(u) for (int e = adj[u], v; e; e = nxt[e]) if ((v = go[e]) != fu)

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

const int N = 5e4 + 5, M = N << 1, E = 155, ZZQ = 10007;

int n, k, ecnt, nxt[M], adj[N], go[M], f[N][E], g[N][E], S[E][E], fac[E];

void add_edge(int u, int v)
{
	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
	nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}

void dfs1(int u, int fu)
{
	int i;
	f[u][0] = 1;
	Tree(u)
	{
		dfs1(v, u);
		f[u][0] += f[v][0];
		For (i, 1, k)
			f[u][i] = (f[u][i] + f[v][i] + f[v][i - 1]) % ZZQ;
	}
}

void dfs2(int u, int fu)
{
	int i;
	For (i, 0, k)
	{
		int tmp = (g[u][i] + (i ? g[u][i - 1] : 0)) % ZZQ;
		Tree(u) tmp = (tmp + f[v][i] + (i ? f[v][i - 1] : 0)) % ZZQ;
		Tree(u) g[v][i] = (tmp - f[v][i] - (i ? f[v][i - 1] : 0)
			+ ZZQ + ZZQ + (!i)) % ZZQ;
	}
	Tree(u) dfs2(v, u);
}

int main()
{
	int i, j, x, y, L, now, A, B, Q;
	n = read(); k = read(); L = read(); now = read(); A = read();
	B = read(); Q = read();
	For (i, 1, n - 1)
	{
		now = (now * A + B) % Q;
		int tmp = i < L ? i : L;
		x = i - now % tmp; y = i + 1;
		add_edge(x, y);
	}
	S[0][0] = fac[0] = 1;
	For (i, 1, k) fac[i] = 1ll * fac[i - 1] * i % ZZQ;
	For (i, 1, k) For (j, 1, i)
		S[i][j] = (S[i - 1][j - 1] + j * S[i - 1][j]) % ZZQ;
	dfs1(1, 0); dfs2(1, 0);
	For (i, 1, n)
	{
		int ans = 0;
		For (j, 0, k)
			ans = (S[k][j] * fac[j] % ZZQ * (f[i][j]
				+ g[i][j] + (j ? g[i][j - 1] : 0)) + ans) % ZZQ;
		printf("%d\n", ans);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值