hdu 4443 Lost

        题意:给出一个连通图,保证只有一个长度在3~30的环,从某一个节点开始走,知道不能走,那么最后那个点就是终点,任意一个点作为起点的概率是相同的,问作为终点概率最大的五个点的概率的和是多少。

        思路:这题的思路还算好想,但是写起来实在太蛋疼,各种公式写起来很容易写错,调了一下午,最后实在算不动了,只好静态差错,终于过了……

                  先考虑没有环的情况,也就是一棵树,对于一棵树来说,我们用两个数组记录它的信息,down[u]表示从u的某一个子树中的点走到u的概率,up[u]表示从u的某个非子树中的点走到u的概率。down[u]=sum{down[v]*(1/childs[v])}+(1/n)*(1/(childs[v]+1)),其中childs[v]是v的儿子的个数,简单来说down[u]就是:从v的某个子树中的点走到v的概率×从v走到u的概率(注意走过的节点是不能再走的),在加上以v为起点走到u的概率。

                  然后是up[u],假设已经算出up[u],那么up[v]=up[u]*(1/childs[u])+(down[u]-tmp)*(1/(childs[u]+1))+(1/n)*(1/(childs[u]+1)),其中tmp是从v及v的子树中某一点到u的概率(这个其实就是down[u]属于v的那一部分),简单来说up[u]就是:从u的非子树的某一点到u的概率×从u到v的概率,再加上从u的其他的子树中上来到u的概率×这种情况下u到v的概率,再加上以u为起点走到v的概率。

                  这样树上的情况就能算出来了,但是要注意的是不能直接这么写,因为有些边界情况和细节问题要处理。。。。

                  然后是环了,首先要找到环,这个简单,直接tarjan就行了,因为只有一个环,情况比较特殊,我YY了一下,然后直接找出了环,然后依次存到数组里。对于环上的点,如果它有子树,那么就从这个点开始来一遍树形dp,把down[u]计算出来。计算完以后,还需要计算的是rt[i][j]和lf[i][j],这两个数组分别代表i点向右走走到j的概率,另一个则是i点向左走走到j的概率。因为环上的点比较少,直接暴力就行了,求的思路和上面的也相似,需要注意的是i这个点要么是从它子树“上”来的,要么就是起点。最后对于一个点来说,如果它没有子树,那么它作为终点的概率就是它左边的点一直向左走走到它或者它右边的点一直向右走走到它的概率。如果它有子树up[u]就等于其他点向左或向右一直走走到它的概率,算出up[u]以后就可以从这个点开始做树形dp把它子树的值也算出来。

                  至此,已经把所有能作为终点的概率都算出来了,能做为终点的点有两种,一种是树上的叶子节点,一种是环上的没有子树的节点,最后统计一下即可。

        这题写起来真的很烦,有很多细节问题需要考虑,上面写的有可能有的细节没有写清楚,可以直接看代码。呼,终于写完了,虽说感觉没什么人看(可能有耐心去做这题的都不多),但写完还是感觉很爽的~

 

 

代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#define inf 0x3f3f3f3f
#define Inf 0x3FFFFFFFFFFFFFFFLL
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
typedef long long ll;
const int maxn=100000+100;
struct Edge
{
    int v,next;
};
Edge edges[maxn<<1];
int head[maxn],nEdge;
void AddEdge(int u,int v)
{
    nEdge++;
    edges[nEdge].v=v;
    edges[nEdge].next=head[u];
    head[u]=nEdge;
}
int pre[maxn],loop[50],id[maxn],len,dfs_clock;
int degree[maxn];
stack<int>S;
int n;
double down[maxn],up[maxn],lf[50][50],rt[50][50];
int childs[maxn];
void Init()
{
    memset(head,0xff,sizeof(head));
    memset(pre,0,sizeof(pre));
    memset(id,0xff,sizeof(id));
    memset(down,0,sizeof(down));
    memset(childs,0,sizeof(childs));
    memset(degree,0,sizeof(degree));
    nEdge=-1;dfs_clock=0;
}
int tarjan(int u,int fa)
{
    int lowu=pre[u]=++dfs_clock;
    S.push(u);
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa) continue;
        if(!pre[v])
        {
            int lowv=tarjan(v,u);
            lowu=min(lowu,lowv);
            if(lowv>pre[u])
            {
                int x;
                while(true)
                {
                    x=S.top();S.pop();
                    if(x==v) break;
                }
            }
        }
        else if(pre[v]<pre[u])
           lowu=min(lowu,pre[v]);
    }
    if(lowu==pre[u])
    {
        int x;
        x=S.top();
        if(x!=u)
        {
            len=0;
            while(true)
            {
                x=S.top();S.pop();
                loop[len]=x;id[x]=len++;
                if(x==u) {S.push(u);break;}
            }
        }
    }
    return lowu;
}

void fdown(int u,int fa)
{
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa||id[v]>=0) continue;
        childs[u]++;
        fdown(v,u);
        if(childs[v]!=0)
          down[u]+=down[v]*(1.0/childs[v]);
        down[u]+=(1.0/n)*(1.0/(childs[v]+1));
    }
}
void fup(int u,int fa,int p)
{
    for(int k=head[u];k!=-1;k=edges[k].next)
    {
        int v=edges[k].v;
        if(v==fa||id[v]>=0) continue;
        double tmp=0;
        if(childs[v]!=0)
          tmp=down[v]*(1.0/childs[v]);
        tmp+=(1.0/n)*(1.0/(childs[v]+1));
        up[v]=up[u]*(1.0/childs[u]);
        if(childs[u]>1)
        {
            if(u==p)
              up[v]+=(down[u]-tmp)*(1.0/(childs[u]+1));
            else up[v]+=(down[u]-tmp)*(1.0/childs[u]);
        }
        if(u!=p) up[v]+=(1.0/n)*(1.0/(childs[u]+1));
        fup(v,u,p);
    }
}
void calloop()
{
    double now,cc;
    int tmp,tmp2;
    for(int i=0;i<len;++i)
    {
        tmp=(i+1)%len;
        now=rt[i][tmp]=down[loop[i]]*(1.0/(childs[loop[i]]+1))+(1.0/n)*(1.0/(childs[loop[i]]+2));
        for(int j=2;j<len;++j)
        {
            tmp2=tmp;
            tmp=(i+j)%len;
            rt[i][tmp]=now*(1.0/(childs[loop[tmp2]]+1));
            now=rt[i][tmp];
        }
        tmp=(i-1+len)%len;
        now=lf[i][tmp]=down[loop[i]]*(1.0/(childs[loop[i]]+1))+(1.0/n)*(1.0/(childs[loop[i]]+2));
        for(int j=2;j<len;++j)
        {
            tmp2=tmp;
            tmp=(i-j+len)%len;
            now=lf[i][tmp]=now*(1.0/(childs[loop[tmp2]]+1));
        }
    }
    for(int i=0;i<len;++i)
    {
        if(childs[loop[i]]==0)
        {
            up[loop[i]]=lf[(i-1+len)%len][i]+rt[(i+1)%len][i];
        }
        else
        {
            cc=0;
            for(int j=0;j<len;++j)
            {
                if(i==j) continue;
                cc+=lf[j][i]+rt[j][i];
            }
            cc-=(rt[(i+1)%len][i]+lf[(i-1+len)%len][i]);
            up[loop[i]]=cc*(1.0*childs[loop[i]]/(childs[loop[i]]+1))+(1.0/n)*(1.0*childs[loop[i]]/(childs[loop[i]]+2));
            up[loop[i]]+=rt[(i+1)%len][i]+lf[(i-1+len)%len][i];
            fup(loop[i],-1,loop[i]);
        }
    }
}
double solve()
{
    double ans=0;
    double res[6]={0};
    tarjan(1,-1);
    for(int i=0;i<len;++i)
       fdown(loop[i],-1);
    calloop();
    for(int i=1;i<=n;++i)
    {
        if(degree[i]==1||(id[i]>=0&&childs[i]==0))
        {
            res[0]=up[i];
            sort(res,res+6);
        }
    }
    for(int i=1;i<6;++i)
      ans+=res[i];
    return ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        Init();
        int u,v;
        for(int i=0;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            degree[u]++;degree[v]++;
            AddEdge(u,v);
            AddEdge(v,u);
        }
        double ans=solve();
        printf("%.5lf\n",ans);
    }
    return 0;
}


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值