BZOJ2878 [Noi2012]迷失游乐园 【基环树 + 树形dp + 期望dp】

题目链接

BZOJ2878

题解

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

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

对于我们枚举的环上的点\(u\),答案自然就是
\[\sum\limits_{(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\n",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\n",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;
}

猜你喜欢

转载自www.cnblogs.com/Mychael/p/9251531.html