[hdu4035]Maze——树上高消

题目大意:

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

1.以 k i k_i ki的概率回到1号节点

2.以 e i e_i ei的概率走出迷宫

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

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

思路:

首先设 p i = 1 − k i − e i p_i=1-k_i-e_i pi=1kiei

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

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

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

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

这个时候再来考虑带1号点的情况,其实只需要将方程表示为 f i = A i × f f a + B i × f 1 + C i f_i=A_i\times f_{fa}+B_i\times f_1+C_i fi=Ai×ffa+Bi×f1+Ci的形式就好了,这样到了一号点之后还需要解一个一次方程。

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

下面是无聊的推式子。

f u = k u × f 1 + p u × ( 1 + f f a + ∑ v f v d u ) f u = k u × f 1 + p u × ( 1 + f f a + ∑ v A v × f u + B v × f 1 + C v d u ) A u = p u d u 1 − p u d u ∑ v A v , B u = k u + p u d u ∑ v B v 1 − p u d u ∑ v A v , C u = p u + p u d u ∑ c C v 1 − p u d u ∑ v A v \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} fufuAu=ku×f1+pu×(1+duffa+vfv)=ku×f1+pu×(1+duffa+vAv×fu+Bv×f1+Cv)=1dupuvAvdupu,Bu=1dupuvAvku+dupuvBv,Cu=1dupuvAvpu+dupucCv

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.10
 * Problem : hdu4035
 * E-mail : ylsoi@foxmail.com
 * ====================================*/
#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;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值