zoukankan      html  css  js  c++  java
  • Luogu P2081 [NOI2012]迷失游乐园 | 期望 DP 基环树

    题目链接

    基环树套路题。(然而各种错误调了好久233)

    当$m=n-1$时,原图是一棵树。

    先以任意点为根做$dp$,求出从每一个点出发,然后只往自己子树里走时路径的期望长度。

    接着再把整棵树再扫一遍,求出从每一个点出发时路径的期望长度。

    最后再统计一遍答案即可。

    当$m=n$时,原图是一棵基环树。

    先找到图中那唯一的一个环,然后以环上的每一个节点为根在此节点的子树内做$dp$。

    环上节点的数量很少,$dp$之后直接暴力算出从每一个环上节点出发时路径的期望长度。

    然后把每一个环上节点的子树扫一遍,求出从每一个点出发时路径的期望长度。

    最后再统计一遍答案即可。

    #include<iostream>
    #include<cstdio>
    #include<fstream>
        using namespace std;
    struct edge
    {
        int last;
        int end;
        int weight;
    }e[200005];
        int n=0,m=0,root=0;
        int ne=1,cnt[100005],note[100005];
        int tot=0,ring[100005];
        int ringdis[100005];
        bool flag=false,vis[100005],inring[100005];
        double ans=0,f[100005],d[100005];
    void NewEdge(int u,int v,int w)
    {
        ne++;
        e[ne].last=note[u];
        e[ne].end=v;
        e[ne].weight=w;
        note[u]=ne;
    }
    void dfs(int x,int fx)
    {
        cnt[x]=f[x]=0;
        for(int i=note[x];i;i=e[i].last)
        {
            if(e[i].end==fx||inring[e[i].end]) continue;
            dfs(e[i].end,x),cnt[x]++;
        }
        if(cnt[x]==0) return;
        for(int i=note[x];i;i=e[i].last)
        {
            if(e[i].end==fx||inring[e[i].end]) continue;
            f[x]+=e[i].weight+f[e[i].end];
        }
        f[x]/=cnt[x];
    }
    void dfss(int x,int fx,int w)
    {
        if(x==root&&!inring[x]) d[x]=f[x];
        else if(x!=root)
        {
            d[x]=f[x]*cnt[x]/(cnt[x]+1);
            if(fx==root&&cnt[fx]==1) d[x]+=(double)w/(cnt[x]+1);
            else if(fx==root) d[x]+=((d[fx]*cnt[fx]-f[x]-w)/(cnt[fx]-1)+w)/(cnt[x]+1);
            else d[x]+=((d[fx]*(cnt[fx]+1)-f[x]-w)/cnt[fx]+w)/(cnt[x]+1);
        }
        for(int i=note[x];i;i=e[i].last)
        {
            if(e[i].end==fx||inring[e[i].end]) continue;
            dfss(e[i].end,x,e[i].weight);
        }
    }
    void dfsss(int x,int fe)
    {
        if(flag) return;
        vis[x]=true,ring[++tot]=x;
        for(int i=note[x];i;i=e[i].last)
        {
            if(i==(fe^1)) continue;
            if(flag) break;
            if(vis[e[i].end])
            {
                int st=0,ed=tot,h=e[i].end;
                for(int i=1;i<=tot;i++)
                    if(ring[i]==h) {st=i;break;}
                for(int i=st;i<=ed;i++)
                {
                    inring[ring[i]]=true;
                    for(int j=note[ring[i]];j;j=e[j].last)
                        if(e[j].end==((i==ed)?ring[st]:ring[i+1]))
                            {ringdis[i]=e[j].weight;break;}
                }
                for(int i=st;i<=ed;i++) dfs(ring[i],0);
                 for(int i=st;i<=ed;i++)
                {
                    int x=ring[i];
                    d[x]=f[x]*cnt[x]/(cnt[x]+2);
                    int u=i,w=0;
                    double p=1;
                    for(;;)
                    {
                        p/=cnt[ring[u]]+((u==i)?2:1),w=ringdis[u];
                        u=((u+1>ed)?st:(u+1));
                        if(((u+1>ed)?st:(u+1))==i) 
                            {d[x]+=p*(f[ring[u]]+w); break;}
                        else d[x]+=p*(f[ring[u]]*cnt[ring[u]]/(cnt[ring[u]]+1)+w);
                    }
                    u=i,w=0,p=1;
                    for(;;)
                    {
                        p/=cnt[ring[u]]+((u==i)?2:1),w=ringdis[(u-1<st)?ed:(u-1)];
                        u=((u-1<st)?ed:(u-1));
                        if(((u-1<st)?ed:(u-1))==i) 
                            {d[x]+=p*(f[ring[u]]+w); break;}
                        else d[x]+=p*(f[ring[u]]*cnt[ring[u]]/(cnt[ring[u]]+1)+w);
                    }
                }
                for(int i=st;i<=ed;i++)
                    cnt[ring[i]]+=2,root=ring[i],dfss(root,0,0);
                flag=true; break;
            }
            dfsss(e[i].end,i);
        }
        tot--;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int u=0,v=0,w=0;
            scanf("%d%d%d",&u,&v,&w);
            NewEdge(u,v,w);
            NewEdge(v,u,w);
        }
        if(m==n-1) root=1,dfs(root,0),dfss(root,0,0);
              else dfsss(1,0);
        for(int i=1;i<=n;i++)
            ans+=(double)1/n*d[i];
        printf("%.5f",ans);
        return 0;
    }
    Luogu P2081
  • 相关阅读:
    从n个数中随机选出k个数,并判断和是不是素数
    最小生成树模板——给出两点和权值
    leetcode 538. 把二叉搜索树转换为累加树 【时间击败100.00%】 【内存击败96.46%】
    leetcode 100. 相同的树【 时间击败100.00%】 【内存击败83.99%】
    leetcode 572. 另一个树的子树 【时间击败88.10%】 【内存击败96.40%】
    java中hashmap的实现原理
    Java的int和Integer
    LeetCode 26. 删除排序数组中的重复项 【时间击败100.00%】 【内存击败95.37%】
    LeetCode 25. K 个一组翻转链表 【时间击败99.35%】 【内存击败74.50%】
    LeetCode 24. 两两交换链表中的节点 【时间击败100.00%】 【内存击败86.60%】
  • 原文地址:https://www.cnblogs.com/wozaixuexi/p/11216153.html
Copyright © 2011-2022 走看看