zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    20145总结
    2014515 总结
    2014514 总结
    20148总结
    20147总结
    20146总结
    20149总结
    2014512 总结
    2014513 总结
    【Visual Lisp】驱动器、目录、文件和注册表
  • 原文地址:https://www.cnblogs.com/Gloid/p/9636960.html
Copyright © 2011-2022 走看看