[bzoj2878][Noi2012]迷失游乐园(基环树dp)

bzoj luogu

题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望。

对于一棵树:

用两个$dp$数组分别记录从这个点起向上向下走的期望

向下走的$dp$不用多说

向上走的$dp$:

对于从$u$计算$v$的dp

$dp[v]$应当是从u向周围引出所有路径减去走向t的路径的期望后再除以$deg_{u}-1$

对于基环树:

环上的点很少。

此时环上的点的向上$dp$指从u出发向环上两头走的期望。

如何计算:对于环上每一个点都向环的两头各dp一次取平均值。

完毕。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100011;
const double eps=1e-8;
template<typename TP>void read(TP &kk){
    #define ak *
    TP phy=0,ioi=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')ioi=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){phy=phy*10+ch-'0';ch=getchar();}
    kk=phy ak ioi;
}
int n,m;
struct sumireko
{
    int to,ne,w;
}e[N*2];
int he[N],ecnt;
void addline(int f,int t,int w)
{
    e[++ecnt].to=t;
    e[ecnt].ne=he[f];
    he[f]=ecnt;
    e[ecnt].w=w;
}
bool onr[N];
double deg[N],sc[N],dpu[N],dpd[N];
void dfs1(int x,int f)
{
    for(int i=he[x],t;i;i=e[i].ne)
    {
        t=e[i].to;
        if(t==f||onr[t]) continue;
        dfs1(t,x);
        dpd[x]+=dpd[t]+e[i].w;
    }
    if(sc[x]>eps) dpd[x]/=sc[x];
}
void dfs2(int x,int f)
{
    for(int i=he[x],t;i;i=e[i].ne)
    {
        t=e[i].to;
        if(f==t||onr[t]) continue;
        dpu[t]=e[i].w;
        if(deg[x]-1.0>eps)dpu[t]+=(dpd[x]*sc[x]-e[i].w-dpd[t]+dpu[x]*(deg[x]-sc[x]))/(deg[x]-1.0);
        dfs2(t,x);
    }
}
int rnd[N],hp;
int sp;bool vv[N];
int find(int x,int f)
{
    if(vv[x]){sp=x;return 1;}
    vv[x]=1;int tmp;
    for(int i=he[x],t;i;i=e[i].ne)
    {
        t=e[i].to;
        if(t==f) continue;
        if(tmp=find(t,x))
        {
            if(tmp==1)
            {
                rnd[++hp]=x;
                onr[x]=1;
                if(x!=sp) return 1;
            }
            return 2;
        }
    }
    return 0;
}
double dpt[N];
void dfs3(int x,int f,int o,int s)
{
    if(!o)
    {
        int g=0;
        for(int i=he[x],t;i;i=e[i].ne)
        {
            t=e[i].to;
            if(!onr[t]) continue;
            g++;dfs3(t,x,g,s);
            dpu[x]+=dpt[t]+e[i].w;
        }
        dpu[x]/=2;
        return;
    }
    double tmp=0;
    dpt[x]=0;
    for(int i=he[x],t;i;i=e[i].ne)
    {
        t=e[i].to;
        if(!onr[t]||t==s||t==f) continue;
        dfs3(t,x,o,s);
        dpt[x]+=dpt[t]+e[i].w;
        tmp+=1;
    }
    if(sc[x]+tmp>eps)
    dpt[x]=(dpt[x]+dpd[x]*sc[x])/(sc[x]+tmp);
}

double ans;
int xi,yi,wi;
int main()
{
    read(n),read(m);
    for(int i=1;i<=m;i++)
    {
        read(xi),read(yi),read(wi);
        addline(xi,yi,wi),addline(yi,xi,wi),deg[xi]+=1,deg[yi]+=1;
    }
    if(m==n-1)
    {
        for(int i=1;i<=n;i++) sc[i]=deg[i]-1;
        sc[1]+=1;
        dfs1(1,0);
        dfs2(1,0);
        for(int i=1;i<=n;i++) ans+=(dpd[i]*sc[i]+dpu[i])/deg[i];
        ans/=n;
        printf("%.5lf\n",ans);
        return 0;
    }
    find(1,0);
    for(int i=1;i<=n;i++) sc[i]=deg[i]-(onr[i]?2.0:1.0);
    for(int i=1;i<=hp;i++) dfs1(rnd[i],0);
    for(int i=1;i<=hp;i++) dfs3(rnd[i],0,0,rnd[i]);
    for(int i=1;i<=hp;i++) dfs2(rnd[i],0);
    for(int i=1;i<=n;i++) ans+=(dpd[i]*sc[i]+dpu[i]*(deg[i]-sc[i]))/deg[i];
    ans/=n;
    printf("%.5lf\n",ans);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/rikurika/p/11271194.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的公寓报修管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本公寓报修管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此公寓报修管理系统利用当下成熟完善的Spring Boot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的MySQL数据库进行程序开发。公寓报修管理系统有管理员,住户,维修人员。管理员可以管理住户信息和维修人员信息,可以审核维修人员的请假信息,住户可以申请维修,可以对维修结果评价,维修人员负责住户提交的维修信息,也可以请假。公寓报修管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:公寓报修管理系统;Spring Boot框架;MySQL;自动化;VUE
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值