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

 

posted @ 2019-07-19 23:06  wozaixuexi  阅读(162)  评论(0编辑  收藏  举报