zoukankan      html  css  js  c++  java
  • BZOJ2878 [Noi2012]迷失游乐园 【基环树 + 树形dp + 期望dp】

    题目链接

    BZOJ2878

    题解

    除了实现起来比较长,思维难度还是挺小的
    观察数据范围发现环长不超过(20),而我们去掉环上任何一个点就可以形成森林
    于是乎我们枚举断掉的点,然后只需求出剩余每个点为根的答案

    (f[i])表示从(i)出发等概率走向子树的期望步数
    如果(i)为根就是我们所需的答案
    首先求出(f[i]),然后用换根法扫一遍便求出每个点为根的答案

    对于我们枚举的环上的点(u),答案自然就是

    [sumlimits_{(u,v) in E} frac{f[v] + w_{(u,v)}}{de[u]} ]

    对于点(u)外向树中的点,再用一次换根法就可以将(u)的答案转移进去,然后求出的就是真实的答案了

    复杂度(O(20n))

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 200005,maxm = 100005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int n,m,de[maxn];
    int h[maxn],ne = 1;
    struct EDGE{int to,nxt; double w;}ed[maxn << 1];
    inline void build(int u,int v,double w){
    	ed[++ne] = (EDGE){v,h[u],w}; h[u] = ne;
    	ed[++ne] = (EDGE){u,h[v],w}; h[v] = ne;
    	de[u]++; de[v]++;
    }
    double f[maxn],up[maxn],ans[maxn];
    int fa[maxn],Out[maxn],rt;
    int c[maxn],ci,vis[maxn],inc[maxn],at[maxn];
    void dfs1(int u){
    	f[u] = 0; vis[u] = true;
    	if (u != rt && de[u] == 1) return;
    	if (!de[u]) return;
    	double d = (u == rt) ? 1.0 / de[u] : 1.0 / (de[u] - 1);
    	Redge(u) if ((to = ed[k].to) != fa[u] && !Out[to]){
    		fa[to] = u; up[to] = ed[k].w; dfs1(to);
    		f[u] += (f[to] + ed[k].w) * d;
    	}
    }
    void dfs2(int u){
    	if (u != rt){
    		int v = fa[u];
    		if (de[v] == 1) f[u] = f[u] * (de[u] - 1) / de[u] + up[u] / de[u];
    		else {
    			double tmp = f[v] * de[v] / (de[v] - 1) - (f[u] + up[u]) / (de[v] - 1);
    			f[u] = f[u] * (de[u] - 1) / de[u] + (tmp + up[u]) / de[u];
    		}
    	}
    	Redge(u) if ((to = ed[k].to) != fa[u] && !Out[to])
    		dfs2(to);
    }
    void solve1(){
    	rt = 1;
    	dfs1(1);
    	dfs2(1);
    	double ans = 0;
    	REP(i,n) ans += f[i];
    	printf("%.5lf
    ",ans / n);
    }
    void dfs3(int u){
    	vis[u] = true;
    	Redge(u) if ((to = ed[k].to) != fa[u]){
    		if (vis[to]){
    			if (ci) continue;
    			for (int i = u; i != to; i = fa[i]) c[++ci] = i,inc[i] = true;
    			c[++ci] = to; inc[to] = true;
    		}
    		else fa[to] = u,dfs3(to);
    	}
    }
    void dfs4(int u){
    	f[u] = 0;
    	if (de[u] == 1) return;
    	double d = 1.0 / (de[u] - 1);
    	Redge(u) if ((to = ed[k].to) != fa[u] && !Out[to]){
    		fa[to] = u; up[to] = ed[k].w; dfs4(to);
    		f[u] += (f[to] + ed[k].w) * d;
    	}
    }
    void dfs5(int u){
    	int v = fa[u];
    	if (de[v] == 1) f[u] = f[u] * (de[u] - 1) / de[u] + up[u] / de[u];
    	else {
    		double tmp = f[v] * de[v] / (de[v] - 1) - (f[u] + up[u]) / (de[v] - 1);
    		f[u] = f[u] * (de[u] - 1) / de[u] + (tmp + up[u]) / de[u];
    	}
    	ans[u] = f[u];
    	Redge(u) if ((to = ed[k].to) != fa[u])
    		dfs5(to);
    }
    void work(int u){
    	Out[u] = true;
    	REP(i,n) vis[i] = false,at[i] = false; vis[u] = true;
    	Redge(u){
    		de[to = ed[k].to]--;
    		if (!inc[to]) at[to] = true;
    	}
    	Redge(u) if (!vis[to = ed[k].to]){
    		fa[rt = to] = 0;
    		dfs1(rt);
    		dfs2(rt);
    	}
    	double d = 1.0 / de[u];
    	Redge(u) ans[u] += (f[to = ed[k].to] + ed[k].w) * d;
    	f[u] = ans[u];
    	Redge(u){
    		de[to = ed[k].to]++;
    		if (!inc[to]){
    			rt = to; fa[to] = u; up[to] = ed[k].w;
    			dfs4(rt);
    			dfs5(rt);
    		}
    	}
    	Out[u] = false;
    }
    void solve2(){
    	dfs3(1);
    	REP(i,ci) work(c[i]);
    	double re = 0;
    	REP(i,n) re += ans[i];
    	re /= n;
    	printf("%.5lf
    ",re);
    }
    int main(){
    	n = read(); m = read(); int a,b,w;
    	REP(i,m){
    		a = read(); b = read(); w = read();
    		build(a,b,w);
    	}
    	if (m == n - 1) solve1();
    	else solve2();
    	return 0;
    }
    
    
  • 相关阅读:
    C++多线程同步技巧(三)--- 互斥体
    Windows核心编程笔记之进程
    HTTP协议之分块传输与分段编码
    CVE-2013-2551:Internet Explore VML COALineDashStyleArray 整数溢出漏洞简单调试分析
    SQLServer数据库及注入方法
    Windows核心编程笔记之内核对象
    Windows核心编程笔记之错误处理
    Windows核心编程笔记之处理字符串
    CVE-2012-0774:Adobe Reader TrueType 字体整数溢出漏洞调试分析
    CVE-2012-1876:Internet Exporter MSHTML.DLL CaculateMinMax 堆溢出简单分析
  • 原文地址:https://www.cnblogs.com/Mychael/p/9251531.html
Copyright © 2011-2022 走看看