P5405 [CTS2019]氪金手游

这应该是CTS2019最简单的题。----lhm

首先假设题目给的是一个单向的链,并且所有 (W) 都是确定的,那么显然第一步一定要选择链的起点,概率为 (frac{W_1}{S_{1...n}});第二步如果选到起点,就再选一次,因此相当于与起点无关,因此合法概率为 (frac{W_2}{S_{2...n}});第三步合法概率为(frac{W_3}{S_{3...n}})...

如果给的是一个外向树,我们发现儿子与儿子之间是互不影响的(显然),那么 (cur) 子树的合法概率是:(sum_{w[...]}frac{W_{cur}}{Sum[cur]}prod_{son}f_{son}),注意这里的(f_{son}) 是在 (son)(W)(w[son]) 的条件下的合法概率。

现在我们考虑不同概率的 (W)。发现子树内具体的 (W) 对父亲的答案没有影响,只有子树的和以及合法概率对答案有影响。因此我们考虑将子树 (W) 和计入状态。我们设 (f[cur][w]) 表示 (cur) 及其子树的 (W) 和为 (w) 的合法概率,然后可以树形背包解决。于是我们有:

(初始化,算入 (W_{cur}) 并设置 (w)

[f[cur][1/2/3]= (1/2/3) * a[1/2/3] ]

(加入一个子树)

[f[cur][i+j]<-f[cur][i] * f[to][j] ]

(每个 (cur) 结束后的操作,意义见上面的式子)

[f[cur][i] <- f[cur][i] / i ]

现在考虑如果有由儿子指向父亲的边该怎么办。正难则反,既然子树之间无关系,那么我们可以求出忽视这条边的限制(直接“删掉”边“分离”出子树)以及这条边是正向边(同上)的“合法”概率,然后相减。即,对于反向边,有:

[f[cur][i] <- f[cur][i] * f[to][j] ]

[f[cur][i + j] <- (-f[cur][i] * f[to][j]) ]

其它不变。

关键代码:

void dfs(int cur, int faa) {
	siz[cur] = 1;
	for (register int i = head[cur]; i; i = e[i].nxt) {
		int to = e[i].to; if (to == faa) continue;
		dfs(to, cur);
		for (register int j = 0; j <= siz[cur] * 3; ++j) {
			for (register int k = 0; k <= siz[to] * 3; ++k) {
				if (i & 1)//son -> father
					ADD(tmp[j], f[cur][j] * f[to][k] % P),
					ADD(tmp[j + k], P - f[cur][j] * f[to][k] % P);
				else//father -> son
					ADD(tmp[j + k], f[cur][j] * f[to][k] % P);
			}
		}
		siz[cur] += siz[to];
		for (register int j = 0; j <= siz[cur] * 3; ++j)
			f[cur][j] = tmp[j], tmp[j] = 0;
	}
	for (register int i = 1; i <= siz[cur] * 3; ++i)
		f[cur][i] = f[cur][i] * inv[i] % P;
}

...

f[i][1] = 1ll * a * inv % P, f[i][2] = 2ll * b * inv % P;
f[i][3] = 3ll * c * inv % P;

一边听课一遍写题解好难受啊

原文地址:https://www.cnblogs.com/JiaZP/p/13422680.html