Loading

题解 HDU4035 Maze

题意

有一个树形的迷宫,你从根节点1出发,每一个节点有\(k_i\)的概率死去,并重新从节点1出发;有\(e_i\)的概率逃脱迷宫,从一个点有相等的概率穿过一次通道到达其他的点,求成功逃脱的期望次数

节点数\(n\leq1e5\)

思路

我们设\(f(i)\)表示从i出发逃脱的期望次数,\(deg_i\)表示节点i的度

一看就是一个期望题嘛,我们考虑如何树形DP

由于求的时候会先算出子节点,我们将子节点与父节点分开来处理

可以得到

\[f(i)=k_if(1)+\frac{1-k_i-e_i}{deg_i}\sum_{j\in son(i)}(f(j)+1)+\frac{1-k_i-e_i}{deg_i}(f(fa_i)+1) \]

然后这个就会互相影响没法做了

一般来说循环转移我们会去写高斯消元对不对,可是这道题你写会炸掉

这样我们考虑一下别的

观察到每一个状态转移方程中都有\(f(1)\),假设我们已经求得\(f(1)\),就可以求解出整个转移系统,且有形式化的\(f_i=A_if(1)+B_if(fa_i)+C_i\)

\(f(j)=A_jf(1)+B_jf(fa_j=i)+C_j\)代入原式得

\[f(i)=k_if(1)+\frac{1-k_i-e_i}{deg_i}\sum_{j\in son(i)}(A_jf(1)+B_jf(i)+C_j+1)+\frac{1-k_i-e_i}{deg_i}(f(fa_i)+1) \]

\(D_i=\frac{1-k_i-e_i}{deg_i}\),然后按照形式整理一下式子

\[(1-d_i\sum_{j\in son(i)}b_j)f_i=(k_i+D_i\sum_{j\in son(i)}A_j)f(1)+D_if(fa_i)+D_i+D_i(\sum_{j\in son(i)}(C_j+1)) \]

\(T_i=1-d_i\sum_{j\in son(i)}b_j\)

\[A_i=\frac{k_i+D_i\sum_{j\in son(i)}A_j}{T_i} \]

\[B_i=\frac{D_i}{T_i} \]

\[C_i=\frac{D_i+D_i(\sum_{j\in son(i)}(C_j+1))}{T_i}=\frac{D_i*deg_i+D_i(\sum_{j\in son(i)}C_j)}{T_i} \]

\(i=1\)时,\(f(1)=A_1f(1)+C_1\),然后\(f(1)=\frac{C_1}{1-A_1}\)

然后在树上跑DP从下往上求出\(A,B,C\)即可,注意若除数为0则无解

注意下文代码中的\(A,B,C\)与上文并非对应关系,准确来说是\(A,B\)反了

推得真辛苦

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int const MAXN=1e5;
double eps=1e-10;
int TT,n,tot,cnt;
double k[MAXN],e[MAXN],B[MAXN],A[MAXN],C[MAXN],d[MAXN],T[MAXN];
int deg[MAXN],h[MAXN];
bool flag;
struct edge{
	int to,next;
}E[MAXN];
void add(int u,int v){
	E[++tot]=(edge){v,h[u]},h[u]=tot,deg[u]++;
}
void dfs(int x,int fa){
	T[x]=1, B[x]=k[x], A[x]=d[x], C[x]=1-k[x]-e[x];
	//if(deg[x]-1==0 && x!=1)return;
	for(int i=h[x];i;i=E[i].next){
		int to=E[i].to;
		if(to==fa)continue;
		dfs(to,x);
		if(flag)return;
		T[x]-=d[x]*A[to];
		B[x]+=d[x]*B[to];
		C[x]+=d[x]*C[to];
	}
	if(fabs(T[x]) < eps){flag=1;return;}
	B[x]/=T[x];A[x]/=T[x];C[x]/=T[x];
	return ;
}
int main(){
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	scanf("%d",&TT);
	while(TT--){
		scanf("%d",&n);
		memset(h,0,sizeof(h));
		memset(deg,0,sizeof(deg));
		flag=0,tot=0;
		for(int i=1,x,y;i<=n-1;i++){
			scanf("%d%d",&x,&y);
			add(x,y);add(y,x);
		}
		for(int i=1;i<=n;i++){
			scanf("%lf%lf",&k[i],&e[i]);
			k[i]/=100.0;e[i]/=100.0;
			//printf("%lf %lf\n",k[i],e[i]);
		}
		for(int i=1;i<=n;i++)
			d[i]=(1-k[i]-e[i])/deg[i];
		dfs(1,0);
		printf("Case %d: ",++cnt);
		//printf("%.18lf %.18lf\n",fabs(1-B[1]),eps);
		if(fabs(1-B[1])<eps || flag){
			printf("impossible\n");
		}else{
			printf("%.6lf\n",C[1]/(1.0-B[1]));
		}
	}
	return 0;
}

后记

这道题体现出了循环转移方程的线性解法,关键在于有一个核心状态与所有状态有关,才能如此求解

posted @ 2020-11-18 22:26  fpjo  阅读(121)  评论(0编辑  收藏  举报