BZOJ2878 NOI2012迷失游乐园(树形dp+环套树+概率期望)

  考虑树的部分分怎么做。令f[i]为i向子树内走的期望路径长度,转移比较显然。算答案时先把其父亲的答案弄好就可以统计自己的答案了。

  环套树也类似。树里直接dp,对环上点暴力考虑环上的每条路径,算完后再在树里统计答案。

  说起来不是很难。事实上想清楚了也确实不是很难。

  不明白为什么不管啥题我都能把代码写的贼长。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 100010
int n,m,p[N],degree[N],nxtlen[N],t=-1;
int dfn[N],low[N],top=0,cnt=0,tot=0,circle[N];
bool flag[N<<1],iscircle[N<<1];
double f[N],g[N],ans=0;
struct data{int to,nxt,len;
}edge[N<<1];
void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;}
void dfs(int k,int from)
{
    for (int i=p[k];~i;i=edge[i].nxt)
    if (edge[i].to!=from&&!iscircle[edge[i].to])
    {
        dfs(edge[i].to,k);
        degree[k]++;
        f[k]+=f[edge[i].to]+edge[i].len;
    }
    if (degree[k]) f[k]/=degree[k];
}
void getans(int k,int from)
{
    for (int i=p[k];~i;i=edge[i].nxt)
    if (edge[i].to!=from)
    {
        g[edge[i].to]=(f[edge[i].to]*degree[edge[i].to]+edge[i].len+(g[k]*(degree[k]+(k>1))-f[edge[i].to]-edge[i].len)/(degree[k]-(k==1)))/(degree[edge[i].to]+1);
        getans(edge[i].to,k);
    }
}
void getans2(int k,int from)
{
    for (int i=p[k];~i;i=edge[i].nxt)
    if (edge[i].to!=from&&!iscircle[edge[i].to])
    {
        g[edge[i].to]=(f[edge[i].to]*degree[edge[i].to]+edge[i].len+(g[k]*(degree[k]+1+(k==from))-f[edge[i].to]-edge[i].len)/(degree[k]+(k==from)))/(degree[edge[i].to]+1);
        getans2(edge[i].to,k);
    }
}
void tarjan(int k,int from)
{
    dfn[k]=low[k]=++cnt;
    for (int i=p[k];~i;i=edge[i].nxt)
    if (edge[i].to!=from)
    {
        if (!dfn[edge[i].to]) tarjan(edge[i].to,k),low[k]=min(low[k],low[edge[i].to]);
        else low[k]=min(low[k],dfn[edge[i].to]);
        if (low[edge[i].to]>dfn[k]) flag[i]=flag[i^1]=1;
    }
}
void findcircle(int k)
{
    circle[++tot]=k;iscircle[k]=1;
    for (int i=p[k];~i;i=edge[i].nxt)
    if (!iscircle[edge[i].to]&&!flag[i]) findcircle(edge[i].to);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj2878.in","r",stdin);
    freopen("bzoj2878.out","w",stdout);
#endif
    n=read(),m=read();
    memset(p,255,sizeof(p));
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read(),z=read();
        addedge(x,y,z),addedge(y,x,z);
    }
    if (m==n-1)
    {
        dfs(1,1);
        g[1]=f[1];
        if (degree[1]>1) getans(1,1);
        else g[edge[p[1]].to]=(f[edge[p[1]].to]*degree[edge[p[1]].to]+edge[p[1]].len)/(degree[edge[p[1]].to]+1),getans(edge[p[1]].to,1);
        for (int i=1;i<=n;i++) ans+=g[i];
    }
    else
    {
        for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i,i);
        for (int i=1;i<=n;i++)
        {
            for (int j=p[i];~j;j=edge[j].nxt)
            if (!flag[j]) {findcircle(i);break;}
            if (tot) break;
        }
        for (int i=1;i<=tot;i++)
            for (int j=p[circle[i]];~j;j=edge[j].nxt)
            if (edge[j].to==circle[i%tot+1]) nxtlen[i]=edge[j].len;
        for (int i=1;i<=tot;i++) dfs(circle[i],circle[i]);
        for (int i=1;i<=tot;i++)
        {
            double P=1;
            int x=i%tot+1,sum=nxtlen[i];
            while (x!=i)
            {
                if (degree[circle[x]])
                {
                    g[circle[i]]+=P*degree[circle[x]]/(degree[circle[x]]+(x%tot+1!=i))*(f[circle[x]]+sum);
                    P/=(degree[circle[x]]+1);
                }
                else if (x%tot+1==i) g[circle[i]]+=P*sum;
                sum+=nxtlen[x];
                x=x%tot+1;
            }
        }
        for (int i=1;i<=tot;i++)
        {
            double P=1;
            int x=i>1?i-1:tot,sum=nxtlen[x];
            while (x!=i)
            {
                if (degree[circle[x]])
                {
                    g[circle[i]]+=P*degree[circle[x]]/(degree[circle[x]]+((x>1?x-1:tot)!=i))*(f[circle[x]]+sum);
                    P/=(degree[circle[x]]+1);
                }
                else if ((x>1?x-1:tot)==i) g[circle[i]]+=P*sum;
                x=x>1?x-1:tot;
                sum+=nxtlen[x];
            }
            g[circle[i]]+=f[circle[i]]*degree[circle[i]];
            g[circle[i]]/=degree[circle[i]]+2;
            getans2(circle[i],circle[i]);
        }
        for (int i=1;i<=n;i++) ans+=g[i];
    }
    printf("%.5lf",ans/n);
    return 0;
}

 

posted @ 2018-09-12 19:59  Gloid  阅读(209)  评论(0编辑  收藏  举报