zoukankan      html  css  js  c++  java
  • LuoGuP4284:[SHOI2014]概率充电器

    Pre

    做了之后看题解发现有人说是一个叫做(up and down)的经典做法。

    (雾)

    Solution

    这道题我是先想成了令(dp[i][0])表示父亲节点没有传入信号时的期望,(dp[i][1])表示父亲节点传入信号时的期望。

    发现没法统计父亲传入的贡献。

    直接统计一个点被激活的概率,不如统计一个点不被激活的概率,再用(1)来减。

    考虑(dp[i])表示这个点没有被激活的期望。并且假装它的父亲节点不存在,也就是只考虑它的子树的贡献。

    那么状态转移

    (dp[i]=(1-p_i)prodlimits_{jin son(i)}EdgeVal_k imes (1-dp[to[j]])+(1-EdgerVal_k))

    具体就是枚举每一个儿子,然后假设边是否可以传导信号,两种情况下转移就是了。

    这样的话,正确性是可以保证的,就是每一个(dp[i])的值是对的,但是现在不能统计出答案。

    然后发现(dp[1])与最终结果中的(dp[1])一样,也就是之后不会再修改(dp[1])了。

    那么考虑哪些点需要被修改。

    一个点被修改一定是父亲节点的贡献没有被统计((1)号节点没有父亲节点)。

    那么考虑把贡献计入每一个点。

    为了再(O(n))时间复杂度完成,需要一些转移。

    假设(pre)表示这个节点的父亲所在的子树的贡献,可以理解为树重构为以(i)为根时,父亲支链上的贡献(此处要考虑相连的这一条边)。

    转移的时候大力转移就可以了。


    至于为什么是正确的,有一个地方。

    因为我们设置的是不被激活的概率,所以在计算的时候不用考虑它对其他节点的贡献。


    Code

    #include <cstdio>
    #include <queue>
    #include <cstring>
    #define ll long long
    #define xx first
    #define yy second
    using namespace std;
    const int N = 500000 + 5;
    int fr[N << 1], to[N << 1], h[N], tot;
    double val[N << 1], p[N];
    double dp[N], dp2[N];
    int n;
    inline void dfs2 (int u, int f, double pre) {
    	dp2[u] = dp[u] * pre;
    	for (int i = h[u]; i; i = fr[i]) {
    		if (to[i] == f) continue;
    		double tmp = pre * dp[u];
    		tmp /= val[i] * dp[to[i]] + (1 - val[i]);
    		tmp = val[i] * tmp + (1 - val[i]);
    		dfs2 (to[i], u, tmp);
    	}
    }
    inline void dfs (int u, int f) {
    	dp[u] = 1 - p[u];
    	for (int i = h[u]; i; i = fr[i]) {
    		if (to[i] == f) continue;
    		dfs (to[i], u);
    		dp[u] *= val[i] * dp[to[i]] + (1 - val[i]);
    	}
    }
    inline void add (int u, int v, double tmp) {
    	tot++;
    	fr[tot] = h[u];
    	to[tot] = v;
    	h[u] = tot;
    	val[tot] = tmp;
    }
    int main () {
    	#ifdef chitongz
    	freopen ("x.in", "r", stdin);
    	#endif
    	scanf ("%d", &n);
    	for (int i = 1; i <= n - 1; ++i) {
    		int x, y;
    		double tp;
    		scanf ("%d%d%lf", &x, &y, &tp);
    		tp /= 100;
    		add (x, y, tp);
    		add (y, x, tp);
    	}
    	for (int i = 1; i <= n; ++i) {
    		scanf ("%lf", &p[i]);
    		p[i] /= 100;
    	}
    	dfs (1, 0);
    	dfs2 (1, 0, 1.0);
    	double ans = 0.0;
    	for (int i = 1; i <= n; ++i) ans += 1 - dp2[i];
    	printf ("%.6lf
    ", ans);
    	return 0;
    }
    

    Conclusion

    思维题。

  • 相关阅读:
    LeetCode(287)Find the Duplicate Number
    LeetCode(290) Word Pattern
    LeetCode(205)Isomorphic Strings
    LeetCode(201) Bitwise AND of Numbers Range
    LeetCode(200) Number of Islands
    LeetCode(220) Contains Duplicate III
    LeetCode(219) Contains Duplicate II
    命令行执行Qt程序
    LeetCode(228) Summary Ranges
    redis 的安装和使用记录
  • 原文地址:https://www.cnblogs.com/ChiTongZ/p/11350298.html
Copyright © 2011-2022 走看看