【Luogu P4284】[SHOI2014]概率充电器

概率充电器

链接:

洛谷

题目大意:

在一棵树上,\(n\) 个节点有直接充电的概率 \(q_i\),边有帮节点间接充电的概率 \(p_{u,v}\)。求每个点充上电的概率。

思路:

直接求充上电的概率不太行,求充不上的概率。设 \(f_u\) 表示 \(u\) 不亮的概率:

\[f_u=(1-q_i)\prod_{(u,v)\in E}(1-p_{u,v}+p_{u,v}f_v) \]

树形 DP 再套换根即可:

\[f_u=(1-q_i)\prod_{v\in\mathrm{son}(u)}(1-p_{u,v}+p_{u,v}f_v) \]

\(g_u\) 表示 \(u\) 为根时的充不上电的答案:

\[g_u=f_u\left(1-p_{\mathrm{fa},u}+p_{\mathrm{fa},u}\left(\frac{g_{\mathrm{fa}}}{1-p_{\mathrm{fa},u}+p_{\mathrm{fa},u}f_u}\right)\right) \]

代码:

纪中机子爆栈,用 BFS 打。


const int N = 2e6 + 10;

inline ll Read()
{
	ll x = 0, f = 1;
	char c = getchar();
	while (c != '-' && (c < '0' || c > '9')) c = getchar();
	if (c == '-') f = -f, c = getchar();
	while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
	return x * f;
}

int n;
double p[N], f[N], g[N];

struct edge
{
	int to, nxt; double val;
}e[N << 1];
int head[N], tot;
void add (int u, int v, double w)
{
	e[++tot] = (edge){v, head[u], w}, head[u] = tot;
}

int q[N], H, T;
int fa[N], TO[N];

void bfs1 ()
{
	memset (fa, -1, sizeof fa);
	memset (q, 0, sizeof q);
	H = 1, T = 0;
	q[++T] = 1;
	fa[1] = 0;
	while(H <= T) 
	{
		int u = q[H++];
		f[u] = 1 - p[u];
		for (int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			if (~fa[v]) continue;
			fa[v] = u, TO[v] = i;
			q[++T] = v;
		}
	}
	for (int i = T; i; i--)
	{
		int u = fa[q[i]], v = q[i], j = TO[v];
		f[u] *= 1 - e[j].val + e[j].val * f[v];
	}
	return;
}

void bfs2 ()
{
	g[1] = f[1];
	memset (q, 0, sizeof q);
	memset (fa, -1, sizeof fa);
	H = 1, T = 0;
	q[++T] = 1;
	fa[1] = 0;
	while(H <= T) 
	{
		int u = q[H++];
		for (int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			if (~fa[v]) continue;
			fa[v] = u;
			g[v] = f[v] * (1 - e[i].val + e[i].val * (g[u] / (1 - e[i].val + e[i].val * f[v])));
			q[++T] = v;
		}
	}
	return;
}

double ans;

int main()
{
	n = Read();
	for (int i = 1; i < n; i++)
	{
		int u = Read(), v = Read(); double w;
		scanf ("%lf", &w); w *= 0.01;
		add(u, v, w), add(v, u, w);
	}
	for (int i = 1; i <= n; i++)
		scanf ("%lf", &p[i]), p[i] *= 0.01;
	bfs1 (), bfs2();
	for (int i = 1; i <= n; i++)
		ans += 1 - g[i];
	printf ("%.6f", ans);
	return 0;
}
posted @ 2021-07-20 19:34  Jayun  阅读(47)  评论(0编辑  收藏  举报