zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    集合的遍历和扩容原理
    最新鲜最详细的Android SDK下载安装及配置教程
    开启IIS Express可以调试X64项目
    C# 正则表达式大全
    Windows下SQLMAP的安装图解
    当前比较流行的前端框架2
    前端组件库1
    服务器Windows 登录 出现401 错误
    IPV6配置
    implicit和 explicit关键字
  • 原文地址:https://www.cnblogs.com/kcn999/p/13863866.html
Copyright © 2011-2022 走看看