Luogu P4284 概率充电器

link

题解

考虑 (u) 进入充电状态需要满足两种条件之一: (exists v in mathrm{subtree}(u)) 直接充电, (exists v ot in mathrm{subtree}(u)) 直接充电。
这是一个很显然的换根dp。
考虑事件 (A)(B) 发生的概率 (P(A+B) = P(A) + P(B) - P(AB)= P(A) + P(B) - P(A) imes P(B))
(不是直接加起来啊!)
(f(u))(u) 进入充电状态的概率 , (g(u))(u) 的子树使 (u) 进入通电状态的概率。
(这里先将题目中的 (p_{u,v},q_u) 除以 (100)
刚开始令 (g(u) gets q_i) ,对于 (u) 的每一个儿子 (v) ,再使 (g(u) gets g(u) + g(v) imes p_{u,v} - g(u) imes g(v) imes p_{u,v})
然后是二次扫描。设 (f_v(u))(u) 不通过 (v) 的子树进入充电状态的概率,容易得到 (f(u) = f_v(u) + g(u) imes p_{u,v} - f_v(u) imes g(u) imes p_{u,v})
整理得到 (f_v(u) = dfrac{f(u) - g(v) imes p_{u,v}}{1 - g(v) imes p_{u,v}})
然后像之前一样, (f(v) gets f_v(u) imes p_{u,v} + g(v) - f_v(u) imes p_{u,v} imes g(v))
注意若 (1 - f(v) imes p_{u,v} = 0) ,则直接令 (f(v) gets g(v))
其实当 (1 - f(v) imes p_{u,v} = 0) 时, (f(v) = p_{u,v} = 1)(v) 是一定会充电的,所以无法用 (f(u)) 求出 (f_v(u)) ,而且也没有更新 (f(v)) 的必要。

#include <cstdio>
#define MAX_N (500000 + 5)

const int eps = 1e-6;
int n;
int h[MAX_N], tot;
struct Edge {
	int to, next;
	double p;
} e[MAX_N << 1];
int q[MAX_N];
double f[MAX_N];

void Add_Edge(int u, int v, int w) {
	e[++tot].to = v;
	e[tot].p = w * 0.01;
	e[tot].next = h[u];
	h[u] = tot;
}

void DFS1(int u, int fa) {
	f[u] = q[u] * 0.01;
	int v;
	double p;
	for (int i = h[u]; i; i = e[i].next) {
		v = e[i].to;
		if (v == fa) continue;
		DFS1(v, u);
		p = f[v] * e[i].p;
		f[u] = f[u] + p - f[u] * p;
	}
}

void DFS2(int u, int fa) {
	int v;
	double p;
	for (int i = h[u]; i; i = e[i].next) {
		v = e[i].to;
		if (v == fa) continue;
		if (f[v] * e[i].p - 1 > eps || 1 - f[v] * e[i].p > eps) {
			p = (f[u] - f[v] * e[i].p) / (1 - f[v] * e[i].p) * e[i].p;
			f[v] = f[v] + p - f[v] * p;
		}
		DFS2(v, u);
	}
}

int main() {
	scanf("%d", &n);
	int u, v, w;
	for (int i = 1; i < n; ++i) {
		scanf("%d%d%d", &u, &v, &w);
		Add_Edge(u, v ,w);
		Add_Edge(v, u, w);
	}
	for (int i = 1; i <= n; ++i)
		scanf("%d", &q[i]);
	DFS1(1, 0);
	DFS2(1, 0);
	double ans = 0;
	for (int i = 1; i <= n; ++i)
		ans += f[i];
	printf("%.6lf
", ans);
	return 0;
}

当然,也可以考虑补集。
(f(u))(u) 进入充电状态的概率 , (g(u))(u) 的子树使 (u) 进入通电状态的概率。
(g(u) = (1-q_u)prodlimits_{vinmathrm{son}(u)}(1 - p_{u,v} + p_{u,v} imes g(v)))
然后就是套路, (f(v) = g(v) imes left(1 - p_{u,v} + p_{u,v} imes dfrac{f(u)}{1 - p_{u,v} + p_{u,v} imes g(v)} ight))
(ans = sumlimits_{i=1}^{n}(1-f(i)))

#include <cstdio>
#define MAX_N (500000 + 5)

const int eps = 1e-6;
int n;
int h[MAX_N], tot;
struct Edge {
	int to, next;
	double p;
} e[MAX_N << 1];
int q[MAX_N];
double f[MAX_N];

void Add_Edge(int u, int v, int w) {
	e[++tot].to = v;
	e[tot].p = w * 0.01;
	e[tot].next = h[u];
	h[u] = tot;
}

void DFS1(int u, int fa) {
	f[u] = 1 - q[u] * 0.01;
	int v;
	for (int i = h[u]; i; i = e[i].next) {
		v = e[i].to;
		if (v == fa) continue;
		DFS1(v, u);
		f[u] *= 1 - e[i].p + f[v] * e[i].p;
	}
}

void DFS2(int u, int fa) {
	int v;
	double p;
	for (int i = h[u]; i; i = e[i].next) {
		v = e[i].to;
		if (v == fa) continue;
		p = f[u] / (1 - e[i].p + f[v] * e[i].p);
		f[v] *= 1 - e[i].p + p * e[i].p;
		DFS2(v, u);
	}
}

int main() {
	scanf("%d", &n);
	int u, v, w;
	for (int i = 1; i < n; ++i) {
		scanf("%d%d%d", &u, &v, &w);
		Add_Edge(u, v ,w);
		Add_Edge(v, u, w);
	}
	for (int i = 1; i <= n; ++i)
		scanf("%d", &q[i]);
	DFS1(1, 0);
	DFS2(1, 0);
	double ans = 0;
	for (int i = 1; i <= n; ++i)
		ans += 1 - f[i];
	printf("%.6lf
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/kcn999/p/13863866.html