BZOJ2878 [Noi2012]迷失游乐园 【基环树 + 树形dp + 期望dp】
题目链接
题解
除了实现起来比较长,思维难度还是挺小的
观察数据范围发现环长不超过\(20\),而我们去掉环上任何一个点就可以形成森林
于是乎我们枚举断掉的点,然后只需求出剩余每个点为根的答案
设\(f[i]\)表示从\(i\)出发等概率走向子树的期望步数
如果\(i\)为根就是我们所需的答案
首先求出\(f[i]\),然后用换根法扫一遍便求出每个点为根的答案
对于我们枚举的环上的点\(u\),答案自然就是
\]
对于点\(u\)外向树中的点,再用一次换根法就可以将\(u\)的答案转移进去,然后求出的就是真实的答案了
复杂度\(O(20n)\)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 200005,maxm = 100005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int n,m,de[maxn];
int h[maxn],ne = 1;
struct EDGE{int to,nxt; double w;}ed[maxn << 1];
inline void build(int u,int v,double w){
ed[++ne] = (EDGE){v,h[u],w}; h[u] = ne;
ed[++ne] = (EDGE){u,h[v],w}; h[v] = ne;
de[u]++; de[v]++;
}
double f[maxn],up[maxn],ans[maxn];
int fa[maxn],Out[maxn],rt;
int c[maxn],ci,vis[maxn],inc[maxn],at[maxn];
void dfs1(int u){
f[u] = 0; vis[u] = true;
if (u != rt && de[u] == 1) return;
if (!de[u]) return;
double d = (u == rt) ? 1.0 / de[u] : 1.0 / (de[u] - 1);
Redge(u) if ((to = ed[k].to) != fa[u] && !Out[to]){
fa[to] = u; up[to] = ed[k].w; dfs1(to);
f[u] += (f[to] + ed[k].w) * d;
}
}
void dfs2(int u){
if (u != rt){
int v = fa[u];
if (de[v] == 1) f[u] = f[u] * (de[u] - 1) / de[u] + up[u] / de[u];
else {
double tmp = f[v] * de[v] / (de[v] - 1) - (f[u] + up[u]) / (de[v] - 1);
f[u] = f[u] * (de[u] - 1) / de[u] + (tmp + up[u]) / de[u];
}
}
Redge(u) if ((to = ed[k].to) != fa[u] && !Out[to])
dfs2(to);
}
void solve1(){
rt = 1;
dfs1(1);
dfs2(1);
double ans = 0;
REP(i,n) ans += f[i];
printf("%.5lf\n",ans / n);
}
void dfs3(int u){
vis[u] = true;
Redge(u) if ((to = ed[k].to) != fa[u]){
if (vis[to]){
if (ci) continue;
for (int i = u; i != to; i = fa[i]) c[++ci] = i,inc[i] = true;
c[++ci] = to; inc[to] = true;
}
else fa[to] = u,dfs3(to);
}
}
void dfs4(int u){
f[u] = 0;
if (de[u] == 1) return;
double d = 1.0 / (de[u] - 1);
Redge(u) if ((to = ed[k].to) != fa[u] && !Out[to]){
fa[to] = u; up[to] = ed[k].w; dfs4(to);
f[u] += (f[to] + ed[k].w) * d;
}
}
void dfs5(int u){
int v = fa[u];
if (de[v] == 1) f[u] = f[u] * (de[u] - 1) / de[u] + up[u] / de[u];
else {
double tmp = f[v] * de[v] / (de[v] - 1) - (f[u] + up[u]) / (de[v] - 1);
f[u] = f[u] * (de[u] - 1) / de[u] + (tmp + up[u]) / de[u];
}
ans[u] = f[u];
Redge(u) if ((to = ed[k].to) != fa[u])
dfs5(to);
}
void work(int u){
Out[u] = true;
REP(i,n) vis[i] = false,at[i] = false; vis[u] = true;
Redge(u){
de[to = ed[k].to]--;
if (!inc[to]) at[to] = true;
}
Redge(u) if (!vis[to = ed[k].to]){
fa[rt = to] = 0;
dfs1(rt);
dfs2(rt);
}
double d = 1.0 / de[u];
Redge(u) ans[u] += (f[to = ed[k].to] + ed[k].w) * d;
f[u] = ans[u];
Redge(u){
de[to = ed[k].to]++;
if (!inc[to]){
rt = to; fa[to] = u; up[to] = ed[k].w;
dfs4(rt);
dfs5(rt);
}
}
Out[u] = false;
}
void solve2(){
dfs3(1);
REP(i,ci) work(c[i]);
double re = 0;
REP(i,n) re += ans[i];
re /= n;
printf("%.5lf\n",re);
}
int main(){
n = read(); m = read(); int a,b,w;
REP(i,m){
a = read(); b = read(); w = read();
build(a,b,w);
}
if (m == n - 1) solve1();
else solve2();
return 0;
}
BZOJ2878 [Noi2012]迷失游乐园 【基环树 + 树形dp + 期望dp】的更多相关文章
- [bzoj2878][Noi2012]迷失游乐园(基环树dp)
[bzoj2878][Noi2012]迷失游乐园(基环树dp) bzoj luogu 题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望. 对于一棵树: 用两个 ...
- [BZOJ2878][NOI2012]迷失游乐园(环套树DP+概率)
推荐讲解:https://www.cnblogs.com/Tunix/p/4561493.html 首先考虑树的情况,就是经典的树上概率DP.先DP出down表示从这个点向儿子走能走的期望长度,再DP ...
- BZOJ2878 NOI2012迷失游乐园(树形dp+环套树+概率期望)
考虑树的部分分怎么做.令f[i]为i向子树内走的期望路径长度,转移比较显然.算答案时先把其父亲的答案弄好就可以统计自己的答案了. 环套树也类似.树里直接dp,对环上点暴力考虑环上的每条路径,算完后再在 ...
- bzoj2878 [Noi2012]迷失游乐园 [树形dp]
Description 放假了,小Z认为呆在家里特别无聊.于是决定一个人去游乐园玩. 进入游乐园后.小Z看了看游乐园的地图,发现能够将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环 ...
- bzoj2878 [Noi2012]迷失游乐园——概率期望DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2878 这个博客写得很好:https://www.cnblogs.com/qt666/p/72 ...
- BZOJ2878 [Noi2012]迷失游乐园
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- 【BZOJ 2878】 2878: [Noi2012]迷失游乐园 (环套树、树形概率DP)
2878: [Noi2012]迷失游乐园 Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m ...
- BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )
一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. -------- ...
- 洛谷 P1453 城市环路 ( 基环树树形dp )
题目链接 题目背景 一座城市,往往会被人们划分为几个区域,例如住宅区.商业区.工业区等等.B市就被分为了以下的两个区域--城市中心和城市郊区.在着这两个区域的中间是一条围绕B市的环路,环路之内便是B市 ...
随机推荐
- 《杜增强讲Unity之Tanks坦克大战》3-添加坦克
3 添加坦克 3.1 本节效果预览 3.2 另存新场景 首先打开上次的场景s1,另存为s2,放到同一个文件夹下面. 3.3 添加坦克模型 在Model文件夹下面找到Tank模型 将Tank ...
- Spring Boot之拦截器与过滤器(完整版)
作者:liuxiaopeng 链接:http://www.cnblogs.com/paddix 作者:蓝精灵lx原文:https://blog.csdn.net/liuxiao723846/artic ...
- vue 动画
Vue 在插入.更新或者移除 DOM 时,提供多种不同方式的应用过渡效果.包括以下几种常见的方式: 在 CSS 过渡和动画中自动应用 class 可以配合使用第三方 CSS 动画库,如 Animate ...
- Swagger本地环境配置
一.技术背景 随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染.先后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远.而前后端的唯一联系便是 API 接口,与此同 ...
- Linux shell中&,&&,|,||的用法
前言 在玩dvwa的命令注入漏洞的时候,遇到了没有预料到的错误,执行 ping 127.0.0.1 & echo "<?php phpinfo(); ?>" & ...
- vim 多个文件切换 :b 命令
MiniBufExplorer插件的使用 博客分类: vim vimMiniBufExplorer 快速浏览和操作Buffer -- 插件: MiniBufExplorer 下载地址 [http:// ...
- C++:类中的赋值函数
先来看一个例子: #include<iostream> #include<string> using namespace std; class Student{ public: ...
- java读取properties文件的几种方法
一.项目中经常会需要读取配置文件(properties文件),因此读取方法总结如下: 1.通过java.util.Properties读取 Properties p=new Properties(); ...
- Java 面试-- 1
JAVA面试精选[Java基础第一部分] 这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了 ...
- C++第一次作业
Github地址点这里