CF802L Send the Fool Further! (hard) 题解

题面自己去看
原版题面:CF802L
洛谷中文题面:CF802L

状态设计

我们令 \(f[i]\) 表示从 \(i\) 号节点开始到达叶子节点的期望距离。
当然我们可以得出对于叶子节点有 \(f[leaf] = 0\)。

暴力做法

显然对于每个节点 \(x\) 我们都有以下转移方程 ( 以下方程中会以下标和中括号两种形式表示数组或函数值以体现更清晰的层次 ) :

\[f[x] = \sum_{y \in \{\{son_x\},\; fa_x\}}\frac{f[y]+dis_{x,\;y}}{deg[x]} \]

通过观察可以发现这个式子中包含了未知量 \(f[fa_x]\)。但每个点都构成这样的一个方程,于是我们有 \(n\) 个未知量和 \(n\) 个方程,所以我们想到用高斯消元来求解,最终 \(f[1]\) 就是答案。

钳制芝士

首先必须明确一点,对于任意一个节点 \(x\) 都有一个关于自己父亲的一次函数表达形式:

\[f[x] = k_x \times f[fa_x] + b_x \]

至于为什么嘛,你自己画棵树自己手推两个你就知道了。

方程推导

我们注意到每个节点 \(x\) 的方程中只有一个未知量 ( 除去他自己 ) 也就是 \(f[fa_x]\),于是我们有了以下推导 ( 这句话我乱胡的,看不懂可以直接看下面的推导 ):

首先我们将上面的式子中节点 \(x\) 的儿子部分和父亲部分分开:

\[f[x] = \sum_{y \in \{son_x\}}\frac{f[y]+dis_{x,\;y}}{deg[x]} + \frac{f[fa_x]+dis_{fa_x,\;x}}{deg[x]} \]

\[=\frac{\sum_{y \in \{son_x\}} (f[y]+dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x})}{deg[x]} \]

然后我们移项,把 \(deg[x]\) 乘到左边去:

\[f[x] \times deg[x] = \sum_{y \in \{son_x\}} (f[y]+dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x}) \]

然后我们将 \(f[x] = k_x \times f[fa_x] + b_x\) 代入到上式的 \(f[y]\) 上去得到:

\[f[x] \times deg[x] = \sum_{y \in \{son_x\}} (k_y * f[x]+ b_y + dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x}) \]

再将 \(Sigma\) 中的东西拆开得:

\[f[x] \times deg[x] = \sum_{y \in \{son_x\}} k_y * f[x ] + \sum_{y \in \{son_x\}} (b_y +dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x}) \]

再把与 \(f[x]\) 有关的式子全部扔到左边去得:

\[f[x] \times deg[x] - \sum_{y \in \{son_x\}} k_y * f[x ] = \sum_{y \in \{son_x\}} (b_y +dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x}) \]

\[\implies f[x] \times deg[x] - f[x] * \sum_{y \in \{son_x\}} k_y = \sum_{y \in \{son_x\}} (b_y +dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x}) \]

\[\implies f[x] \times (deg[x] - \sum_{y \in \{son_x\}} k_y) = \sum_{y \in \{son_x\}} (b_y +dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x}) \]

然后再移项把左边的 \(deg[x] - \sum_{y \in \{son_x\}} k_y\) 除到右边去:

\[f[x] = \frac{\sum_{y \in \{son_x\}} (b_y +dis_{x,\;y}) \;+ \;(f[fa_x]+dis_{fa_x,\;x})}{deg[x] - \sum_{y \in \{son_x\}} k_y} \]

最后把式子整理成 \(f[x] = k_x \times f[fa_x] + b_x\) 的形式:

\[f[x] = \frac{1}{deg[x] - \sum_{y \in \{son_x\}} k_y} \times f[fa_x] + \frac{\sum_{y \in \{son_x\}} (b_y +dis_{x,\;y}) \;+ \;dis_{fa_x,\;x}}{deg[x] - \sum_{y \in \{son_x\}} k_y} \]

于是我们可以得到递推式:

\[k_x = \frac{1}{deg[x] - \sum_{y \in \{son_x\}} k_y} \]

\[b_x = \frac{\sum_{y \in \{son_x\}} (b_y +dis_{x,\;y}) \;+ \;dis_{fa_x,\;x}}{deg[x] - \sum_{y \in \{son_x\}} k_y} \]

最后由于 根节点 1 号节点没有父亲,也就是说 \(f[1] = b[1]\)。所以我们只需要在 dfs 时递归求解 \(k_x\) 和 \(b_x\) 即可 ( \(b_x\) 的方程中含有 \(k_y\) ,所以两个都需要计算),而不需要单独计算 \(f_x\)。

注意

原题中的点标号是从 0 开始的!!!(什么恶心东西)

dfs 部分关键代码

void dfs(int x, int fa)
{
	if(deg[x] == 1 && x != 1) {
		k[x] = b[x] = 0;
		return ;
	}
	
	int sumk = 0;
	for(int i = e.hd[x]; i; i = e.nxt[i]) {
		int y = e.to[i], z = e.w[i];
		if(y == fa) {
			b[x] = pls(b[x], z);
			continue;
		}
		
		dfs(y, x);
		b[x] = pls(b[x], b[y]);//这里是加同时取模操作
		b[x] = pls(b[x], z);
		sumk = pls(sumk, k[y]);
	}
	
	k[x] = qpow(dec(deg[x], sumk), MOD - 2);
	b[x] = mul(b[x], k[x]);//同理
}

完整代码

//省去头文件和快读

int n;
struct Edge {
	int hd[MAXN];
	int nxt[MAXN << 1], to[MAXN << 1];
	int w[MAXN << 1];
	int tot = 0;
	
	void Add(int x, int y, int z) {
		nxt[++tot] = hd[x];
		hd[x] = tot;
		to[tot] = y;
		w[tot] = z;
	}
}e;
int deg[MAXN];

//------------以下是一系列的取模操作------------
int pls(int x, int y)
{
	if(x + y >= MOD)
		return x + y - MOD;
	else
		return x + y;
}
int dec(int x, int y) 
{
	if(x - y < 0)
		return x - y + MOD;
	else
		return x - y;
}
int mul(int x, int y) 
{
	if(1ll * x * y >= 1ll * MOD)
		return 1ll * x * y % MOD;
	else
		return x * y;
}
int qpow(int x, int y)
{
	int val = 1;
	while(y) {
		if(y & 1)
			val = mul(val, x);
		
		x = mul(x, x);
		y >>= 1;
	}
	return val;
}
//------------以上是一系列的取模操作------------

int k[MAXN], b[MAXN];
void dfs(int x, int fa)
{
	if(deg[x] == 1 && x != 1) {
		k[x] = b[x] = 0;
		return ;
	}
	
	int sumk = 0;
	for(int i = e.hd[x]; i; i = e.nxt[i]) {
		int y = e.to[i], z = e.w[i];
		if(y == fa) {
			b[x] = pls(b[x], z);
			continue;
		}
		
		dfs(y, x);
		b[x] = pls(b[x], b[y]);
		b[x] = pls(b[x], z);
		sumk = pls(sumk, k[y]);
	}
	
	k[x] = qpow(dec(deg[x], sumk), MOD - 2);//注意要取逆元
	b[x] = mul(b[x], k[x]);
}

int main()
{
	n = inpt();
	for(int i = 1; i < n; i++) {
		int x = inpt() + 1, y = inpt() + 1, z = inpt();
		
		e.Add(x, y, z);
		e.Add(y, x, z);
		deg[x]++, deg[y]++;
	}//对于所有节点都可以式子化为f[x] = k[x] * f[fa] + b[x]
	
	dfs(1, 0);
	
	printf("%d", b[1]);//因为根节点没有父亲,所以 f[1] = b[1] 
	
	return 0;
}

posted @ 2022-07-28 22:13  YunYuanWang  阅读(54)  评论(0)    收藏  举报