题目链接

基环树套路题。(然而各种错误调了好久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

Luogu P2081 [NOI2012]迷失游乐园 | 期望 DP 基环树的更多相关文章

  1. 【题解】Luogu P2081 [NOI2012]迷失游乐园

    原题传送门 这是当时冬令营课上讲的题,咕咕咕到了现在 如果这题没有环套树的话,就很套路了 需要两个数组up[i]和down[i],down[i]表示从i点第一步向下走的期望距离,up[i]表示从i点第 ...

  2. BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )

    一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. -------- ...

  3. [luogu2081 NOI2012] 迷失游乐园 (树形期望dp 基环树)

    传送门 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m ...

  4. bzoj 2878: [Noi2012]迷失游乐园【树上期望dp+基环树】

    参考:https://blog.csdn.net/shiyukun1998/article/details/44684947 先看对于树的情况 设d[u]为点u向儿子走的期望长度和,du[u]为u点的 ...

  5. bzoj2878 [Noi2012]迷失游乐园 [树形dp]

    Description 放假了,小Z认为呆在家里特别无聊.于是决定一个人去游乐园玩. 进入游乐园后.小Z看了看游乐园的地图,发现能够将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环 ...

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

    [bzoj2878][Noi2012]迷失游乐园(基环树dp) bzoj luogu 题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望. 对于一棵树: 用两个 ...

  7. 【BZOJ 2878】 2878: [Noi2012]迷失游乐园 (环套树、树形概率DP)

    2878: [Noi2012]迷失游乐园 Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m ...

  8. bzoj 2878: [Noi2012]迷失游乐园

    #include<iostream> #include<cstring> #include<cstdio> #define M 100005 #define ld ...

  9. Hdu第八场 树形dp+基环树

    Card Game 每个牌背面的数字朝正面的数字连一条有向边 则题目变为问你最少翻转多少次 能使得每个数字的入度不超过1 首先判断图中每个连通块是不是树或者基环树 因为只有树或者基环树能使得每个点的入 ...

随机推荐

  1. vue-cookies使用

    一.安装 vue-cookies npm install vue-cookies --save 二.引入并声明使用 import Vue form 'Vue' import VueCookies fr ...

  2. STM32F103C8T6使用SPI接口驱动WS2812b灯条

    之前一篇文章写了使用IO控制WS2812b操作原理,但是由于IO的输出比较慢,所以现在改用了硬件SPI控制WS2812b灯条 把SPI的mosi线接到ws2812b的数据线,SPI的速率可达十几Mbi ...

  3. 255 day03_List、Set、数据结构、Collections

    day03 [List.Set.数据结构.Collections] 主要内容 数据结构 List集合 Set集合 Collections 教学目标 [ ] 能够说出List集合特点 [ ] 能够说出常 ...

  4. 一个简单的session传值学习

    a.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  5. Grid 网格布局详解

    Grid网格布局详解: Grid布局与Flex布局有着一定的相似性,Grid布局是将容器划分成行和列,产生单元格,可以看做是二维布局. 基本概念: 采用网格布局的区域,称为"容器" ...

  6. Nginx系列(1)- Nginx简介

    公司产品出现瓶颈 公司项目刚上线的时候,并发量小,用户使用少,所以在低并发的情况下,一个jar包启动应用就够了,然后内部tomcat返回内容给用户 但是慢慢的,使用平台的用户越来越多,并发量慢慢增大了 ...

  7. 搞不定 NodeJS 内存泄漏?先从了解垃圾回收开始

    通常来说,内存管理有两种方式,一种是手动管理,一种是自动管理. 手动管理需要开发者自己管理内存,什么时候申请内存空间,什么时候释放都需要小心处理,否则容易形成内存泄漏和指针乱飞的局面.C 语言开发是典 ...

  8. python处理权限关系一些注意事项

    在模型类中多对多的关系(必须是对象) 如果要获取多对多表中的值,必须使用对象进行搜索,不能直接输入名称 例:这样才能查找到(下面例子是针对,组设置多对多关到用户,现在要根据用户获取相应的组) user ...

  9. 如何一次性add library to classpath

    前言:导入项目时,时常需要手动导包,提示"add library to classpath",需要一个个找报红的类 点击添加本地项目包

  10. HashMap的tableSizeFor解析

    我们都知道,对于HashMap来说,数组的容量为2的倍数,但是我们可以在创建map的时候传入一个数组的大小 此时,这个初始化数组大小会传给一个双参的构造器 1. 创建HashMap public st ...