「NOI2012」迷失游乐园 - 环套树+期望Dp | Bill Yang's Blog

「NOI2012」迷失游乐园 - 环套树+期望Dp

题目大意

    在一棵树或环套树上等概率选择起点行走,每次等概率地前往一个还没去过的相邻点,求路径的期望长度。


题目分析

先考虑树的情况,用$f[]$记录答案,用$d[]$记录$f[]+e.dist$。
这样就可以快速的转移起点统计所有点的$f[]$了。

再考虑环上的情况,先对所有环的上树根进行第一次树形动规,然后在环上分为两条有向链转移答案,再进行第二次树形动规向下传递答案。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=100005;
struct Edge {
int from,to,dist;
};
int n,m,root,Degree[maxn];
double f[maxn],d[maxn];
bool onCircle[maxn];
vector<Edge> edges[maxn];
void AddEdge(int x,int y,int v) {
edges[x].push_back((Edge) {x,y,v});
}
void Dfs1(int Now,int father) {
for(Edge& e:edges[Now]) {
int Next=e.to;
if(Next==father||onCircle[Next])continue;
Degree[Now]++;
Dfs1(Next,Now);
d[Now]+=f[Next]+e.dist;
}
if(Degree[Now])f[Now]=d[Now]/Degree[Now];
if(Now!=root)Degree[Now]++;
}
void Dfs2(int Now,int father) {
for(Edge& e:edges[Now]) {
int Next=e.to;
if(Next==father||onCircle[Next])continue;
d[Next]+=(d[Now]-f[Next]-e.dist)/max(1,Degree[Now]-1)+e.dist;
Dfs2(Next,Now);
}
}
namespace Tree {
void solve() {
root=1;
Dfs1(1,0);
Dfs2(1,0);
}
}
namespace CircleAndTree {
int Dfn[maxn],father[maxn],step=0;
double g[maxn],h[maxn];
bool bj=0;
void Find_Circle(int Now) {
Dfn[Now]=++step;
for(Edge& e:edges[Now]) {
int Next=e.to;
if(!Dfn[Next]) {
father[Next]=Now;
Find_Circle(Next);
} else if(father[Now]!=Next&&Dfn[Next]<Dfn[Now]) {
onCircle[Next]=1;
while(Now!=Next) {
onCircle[Now]=1;
Now=father[Now];
}
bj=1;
return;
}
if(bj)return;
}
}
void Dfs(int Now,int father) {
bool bj=0;
g[Now]=0;
for(Edge& e:edges[Now]) {
int Next=e.to;
if(Next==root||Next==father||!onCircle[Next])continue;
bj=1;
Dfs(Next,Now);
g[Now]+=g[Next]+e.dist;
}
if(Now==root)return;
if(!bj)g[Now]=d[Now]/max(Degree[Now],1);
else g[Now]=(g[Now]+d[Now])/(Degree[Now]+1);
}
void solve() {
Find_Circle(1);
for(int i=1; i<=n; i++)
if(onCircle[i]) {
root=i;
Dfs1(i,0);
}
for(int i=1; i<=n; i++)
if(onCircle[i]) {
root=i;
Dfs(i,0);
h[i]=g[i];
}
for(int i=1; i<=n; i++)
if(onCircle[i]) {
Degree[i]+=2;
d[i]+=h[i];
}
for(int i=1; i<=n; i++)
if(onCircle[i])Dfs2(i,0);
}
}
int main() {
n=Get_Int();
m=Get_Int();
for(int i=1; i<=m; i++) {
int x=Get_Int(),y=Get_Int(),v=Get_Int();
AddEdge(x,y,v);
AddEdge(y,x,v);
}
if(m==n-1)Tree::solve();
else CircleAndTree::solve();
double ans=0;
for(int i=1; i<=n; i++)ans+=d[i]/Degree[i];
printf("%0.5lf\n",ans/n);
return 0;
}
本篇文章有用吗?有用还不快点击!
0%