因为该图要么是一棵书 要么是一个环 所以我们分开处理 从较为简单的树入手
part1:题目转化为 求一棵树上从任何一个节点出发的最长路径的期望值
显然我们可以发现全局期望值等于每个点的期望值d[i]的和除以点的数量 所以我们需要处理出每个点出发的最长路径的期望值
对于任何一个点来说 他有两种行走的方向 一种是向父亲行走 一种是向儿子行走 我们假设d[i] 表示i出发向所有方向的路径期望总和, 用du保存每个节点在树中的邻接点的数量(注意 是在树中而不是在图中 这个定义将对处理环时产生影响)
首先我们需要处理向儿子行走的期望值 用f[i]表示从i出发向以i为根的子树行走的期望值则有 f[i] = sigema ( f[j] + w(i, j) ) / (d[u] - 1) = d[i] / (du[i] - 1) ; (注意 此时d[i]仅包含向子树行走的路径和)
然后我们需要处理向父亲行走的期望值 由于每个节点的f[i]已经处理出来了 对于我们之后的计算方便了很多 对于每一个点i来说 d[i] += (d[fa] - f[i] - w(fa(i), i)) / (du[fa]-1) + wt;
得到这些信息后我们就可以处理树的问题了
part2:剩下我们就需要处理环图的问题了
首先我们可以发现环上的点是难以处理的 我们只能先处理环上点的子树 那么我们就需要把环上的点先找出来并标记好
然后像处理树一样处理每一个环上节点为根的子树
问题在于处理环上点的关系 我们发现 环上的点最多只有20个 这告诉我们可以选择一种相对暴力的方法来处理环的问题
对于每个环上的点 在环上进行dp 我们定义g[i]为第一条边沿着环边走的期望值 则有
g[i] = sigema(g[j] + w(i, j)) / (du[i] + 1)
最后统计答案即可 需要注意的是统计答案时每一个环上节点i的邻接点数其实是du[i]+2, 计算期望时需要注意
至此 本题得到解决
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#define SF scanf
#define PF printf
using namespace std;
typedef long long LL;
typedef long double LD;
const int MAXN = 100000;
int n, m, du[MAXN+10], rt;
int vis[MAXN+10], fa[MAXN+10];
bool cir[MAXN+10];
// Graphs
struct Node {
int v, wt, next;
} Edge[MAXN*2+10];
int adj[MAXN+10], ncnt;
void addedge(int u, int v, int wt) {
Node &e = Edge[++ncnt];
e.v = v; e.wt = wt;
e.next = adj[u]; adj[u] = ncnt;
}
void add(int u, int v, int wt) { addedge(u, v, wt); addedge(v, u, wt); }
// Get Trees' E
LD d[MAXN+10], f[MAXN+10]; // f向下 d总
void Tree_dp(int u) {
d[u] = f[u] = 0;
vis[u] = 1; du[u] = 0;
for(int i = adj[u]; ~i; i = Edge[i].next) {
int v = Edge[i].v, wt = Edge[i].wt;
if(vis[v] || cir[v]) continue;
Tree_dp(v);
du[u]++; d[u] += f[v] + wt;
}
if(du[u]) f[u] = d[u] / du[u];
du[u] += u != rt;
}
// DP father's E
void Fa_dp(int u) {
vis[u] = 1;
for(int i = adj[u]; ~i; i = Edge[i].next) {
int v = Edge[i].v, wt = Edge[i].wt;
if(vis[v] || cir[v]) continue ;
int tot = du[u] - 1;
if(tot == 0) tot++;
d[v] += (LD) (d[u] - f[v] - wt) / (LD) tot + wt;
Fa_dp(v);
}
}
// Find circles
bool Find(int u, int pre) {
vis[u] = 1;
for(int i = adj[u]; ~i; i = Edge[i].next) {
int v = Edge[i].v, wt = Edge[i].wt;
if(!vis[v]) {
fa[v] = u;
if(Find(v, u)) return true;
}
else if(v != fa[u]) {
int x = u;
cir[v] = true;
while(x != v) {
cir[x] = true;
x = fa[x];
}
return true;
}
}
return true;
}
// dp on the circle
LD g[MAXN+10];
int st;
void dp(int u, int pre) {
bool end = true;
g[u] = 0;
for(int i = adj[u]; ~i; i = Edge[i].next) {
int v = Edge[i].v, wt = Edge[i].wt;
if(v == st || v == pre || !cir[v]) continue ;
end = false;
dp(v, u);
g[u] += g[v] + wt;
}
int tot = du[u];
if(!tot) tot++;
if(end) g[u] = (LD) d[u] / tot;
else {
tot = du[u] + 1;
if(u != st) g[u] = (LD) (g[u] + d[u]) / tot;
else return ;
}
}
// Main()
LD Cird[MAXN+10];
int main()
{
memset(adj, -1, sizeof(adj));
double ans = 0;
SF("%d%d", &n, &m);
for(int i = 1; i <= m; i++) {
int u, v, wt;
SF("%d%d%d", &u, &v, &wt);
add(u, v, wt);
}
if(n == m+1) {
rt = 1;
Tree_dp(1);
memset(vis, 0, sizeof(vis));
Fa_dp(1);
}
else {
Find(1, 0);
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++)
if(cir[i])
rt = i, Tree_dp(i);
for(int i = 1; i <= n; i++)
if(cir[i]) {
st = i;
dp(i, 0);
Cird[i] = g[i];
}
for(int i = 1; i <= n; i++)
if(cir[i])
du[i] += 2, d[i] += Cird[i];
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++)
if(cir[i])
Fa_dp(i);
}
for(int i = 1; i <= n; i++)
ans = ans + d[i] / du[i];
PF("%.5f", ans / n);
}
/*
4 4
1 2 3
2 3 1
3 4 4
4 1 5
*/