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;
}

原文地址:https://www.cnblogs.com/Mychael/p/9251531.html