[hdu4035]Maze——树上高消

题目大意:

给定一颗树,从1号节点开始,在每个节点都有三种可能:

1.以\(k_i\)的概率回到1号节点

2.以\(e_i\)的概率走出迷宫

3.和该点相连的边随机走一条

求走出迷宫期望下走的步数。

思路:

首先设\(p_i=1-k_i-e_i\)。

设从第\(i\)个点出发,期望意义下走出迷宫需要走的步数为\(f_i\),那么我们得到的递推式是:
\[
f_i=k_i\times f_1+p_i\times (1+\frac{f_{fa}+\sum_vf_v}{d_i})
\]
不难发现这个递推式我们无法直接求解,它本质上是一个方程组,我们需要将这个方程组解开才能得到答案。

想到解方程组,大家都会想到高斯消元,但是普通的高斯消元在这个题目上显然不太行得通,因为它的时间复杂度为\(n^3\)。

考虑这个方程组有哪些特殊的性质,为了方便理解,我们先不考虑每个点的方程中和1号点有关的部分,这样以后可以发现方程其实也是一个树型结构,每一个点的方程关系到的变量只有它周围的点和它自己,这就使得我们可以尝试用线性的消元去解决这个问题。

考虑将1号点作为根之后,所有叶子节点它的方程中可以表示为:\(f_i=A_i\times f_{fa}+B_i\),那么我们如果在树上采用从子树递推的方式,当\(u\)的所有儿子都可以表示为上述形式的时候,我们直接采用带入消元的方法将它的所有儿子消去,由于带入的元素必定是它自己,\(u\)号点也成功地变成了上面这种形式。这个时候只要一直带入就可以得到1号点最终的\(dp\)值了。

这个时候再来考虑带1号点的情况,其实只需要将方程表示为\(f_i=A_i\times f_{fa}+B_i\times f_1+C_i\)的形式就好了,这样到了一号点之后还需要解一个一次方程。

这种消元的方式称作树上高斯消元。

下面是无聊的推式子。

\[
\begin{aligned}
f_u&=k_u\times f_1+p_u\times (1+\frac{f_{fa}+\sum_vf_v}{d_u})\f_u&=k_u\times f_1+p_u\times (1+\frac{f_{fa}+\sum_vA_v\times f_u+B_v\times f_1+C_v}{d_u})\A_{u}&=\frac{\frac{p_u}{d_u}}{1-\frac{p_u}{d_u}\sum_vA_v},
B_{u}=\frac{k_u+\frac{p_u}{d_u}\sum_vB_v}{1-\frac{p_u}{d_u}\sum_vA_v},
C_{u}=\frac{p_u+\frac{p_u}{d_u}\sum_cC_v}{1-\frac{p_u}{d_u}\sum_vA_v}
\end{aligned}
\]

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.10
 * Problem : hdu4035
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("hdu4035.in","r",stdin);
    freopen("hdu4035.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T f=1; char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    _*=f;
}

const int maxn=1e4+10;
int T,n,d[maxn];
vector<int>G[maxn];
double k[maxn],e[maxn],p[maxn],A[maxn],B[maxn],C[maxn];

void dfs(int u,int fh){
    double sa=0,sb=0,sc=0;
    REP(i,0,G[u].size()-1){
        int v=G[u][i];
        if(v==fh)continue;
        dfs(v,u);
        sa+=A[v];
        sb+=B[v];
        sc+=C[v];
    }
    double mul=p[u]/d[u];
    A[u]=mul/(1-mul*sa);
    B[u]=(k[u]+mul*sb)/(1-mul*sa);
    C[u]=(p[u]+mul*sc)/(1-mul*sa);
}

bool judge(int u,int fh){
    if(e[u]!=0)return true;
    if(p[u]==0)return false;
    REP(i,0,G[u].size()-1){
        int v=G[u][i];
        if(v==fh)continue;
        if(judge(v,u))return true;
    }
    return false;
}

int main(){
    File();
    read(T);
    REP(ca,1,T){
        read(n);
        int u,v;
        REP(i,1,n-1){
            read(u),read(v);
            G[u].pb(v),++d[v];
            G[v].pb(u),++d[u];
        }
        REP(i,1,n){
            scanf("%lf%lf",&k[i],&e[i]);
            k[i]/=100,e[i]/=100;
            p[i]=1-k[i]-e[i];
        }
        if(!judge(1,0))printf("Case %d: impossible\n",ca);
        else{
            dfs(1,0);
            printf("Case %d: %.10lf\n",ca,C[1]/(1-B[1]));
        }
        REP(i,1,n)G[i].clear();
        memset(d,0,sizeof(d));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10358681.html

时间: 2024-11-08 20:49:39

[hdu4035]Maze——树上高消的相关文章

【bzoj4184】shallot 线段树+高斯消元动态维护线性基

题目描述 小苗去市场上买了一捆小葱苗,她突然一时兴起,于是她在每颗小葱苗上写上一个数字,然后把小葱叫过来玩游戏. 每个时刻她会给小葱一颗小葱苗或者是从小葱手里拿走一颗小葱苗,并且 让小葱从自己手中的小葱苗里选出一些小葱苗使得选出的小葱苗上的数字的异或和最大. 这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为Oi选手的你,你能帮帮他吗? 你只需要输出最大的异或和即可,若小葱手中没有小葱苗则输出0. 输入 第一行一个正整数n表示总时间:第二行n个整数a1,a2...an,

BZOJ 2466: [中山市选2009]树( 高斯消元 )

高斯消元解异或方程组...然后对自由元进行暴搜.树形dp应该也是可以的... -------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<bitset> using namespace std; const int ma

[题解]HDU4035 Maze

传 题目描述 一棵\(n\)个节点的树,从1号结点开始游戏,在每一个点\(x\): 有\(a[x]/100\)的可能掉进陷阱死翘翘回到1重新开始 有\(b[x]/100\)的可能找到出口并结束游戏 剩下的可能中,你等概率随机选一条和它相连的边(可以是父亲)走过去 问期望多少步结束游戏 \(1\leq T \leq 30,1\leq n \leq 10000\) 分析 设\(dp[x]\)表示位于\(x\)节点时,期望走几步才能结束游戏 那么\(dp[x]=0.01a[x]*dp[1]+0.01b

【BZOJ2466】【中山市选2009】树 高斯消元解异或方程组

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44356273"); } 题解: 参照此题解,也是我写的,俩题一样. [POJ1681]Painter's Problem 高斯消元,求最小∑系数的异或方程组 代码: #include <cmath> #include &

BZOJ 2466 中山市选2009 树 高斯消元+暴力

题目大意:树上拉灯游戏 高斯消元解异或方程组,对于全部的自由元暴力2^n枚举状态,代入计算 这做法真是一点也不优雅... #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 110 using namespace std; int n,m; int f[M][M],is_free[M],tot; int ans[M],cnt; void

HDU4035 Maze(期望DP)

题意 抄袭自https://www.cnblogs.com/Paul-Guderian/p/7624039.html 有n个房间,由n-1条隧道连通起来,形成一棵树,从结点1出发,开始走,在每个结点i都有3种可能(概率之和为1):1.被杀死,回到结点1处(概率为ki)2.找到出口,走出迷宫 (概率为ei)3.和该点相连有m条边,随机走一条求:走出迷宫所要走的边数的期望值.(2≤n≤10000) Sol 非常nice的一道题. 我简单的说一下思路:首先列出方程,$f[i]$表示在第$i$个位置走出

[hdu4035] Maze【概率dp 数学期望】

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4035 真的是一道好题,题解比较麻烦,我自己在纸上写了好大一块草稿才搞出来,不用公式编辑器的话就很难看清楚,所以不上题解啦,贴一个题解的链接:http://blog.csdn.net/balloons2012/article/details/7891054 注意此题卡精度,我一开始eps是1e-8,WA掉了,开到了1e-10,AC~,真是烦卡精度的题. #include <cstdio> #inclu

HDU-4035 Maze (概率DP求期望)

题目大意:在一个树形迷宫中,以房间为节点.有n间房间,每间房间存在陷阱的概率为ki,存在出口的概率为ei,如果这两种情况都不存在(概率为pi),那么只能做出选择走向下一个房间(包括可能会走向上一个房间).根节点为1,当遇到陷阱时必须返回到根节点1处重新开始,当遇到出口时,走出迷宫.问从开始到走出迷宫所做出选择次数的期望值. 题目分析:定义状态dp(i)表示在节点 i 处直到走出迷宫的选择次数期望值.则状态转移方程为: dp(i)=ki*dp(1)+(1/m)*pi*∑(dp(son)+1) (i

HDU4035 Maze 【树形DP】【期望DP】

题目分析: 以前一直不会这个方法, 我好菜啊. 转移分为三个部分,一个是直接成功,一个是转移到E1,还有一个是转移到自己周围的一圈儿点. 如果是叶子那么只能转移到父亲,如果不是叶子可以把非叶子的转移代换,这样也只转移到父亲,判一下无解就行了. 代码: 1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 10200; 5 const double eps = 1e-10; 6 7 int n,Tmp,cas;