题解 HDU4035 【Maze】

posted on 2022-08-17 12:33:51 | under 题解 | source

problem

https://vjudge.net/problem/HDU-4035

在一棵树上随机游走,从根节点出发,每次有 ku 的几率回到根节点,eu 的几率到达出口,否则随机选择一个与它相连的节点并走过去,求期望多少步能走到出口。1n104

solution

令根为 1tu=1kueu,记 fu 表示从点 u 出发走到出口的期望步数,pu 的父亲,vu 的儿子,d 为点 u 的度数。显然有:

fu=ku×f1+tu×vson(u)d1(fv+1)+tu×d1(fp+1)=ku×f1+tu×d1×(fp+1+vson(u)(fv+1))=ku×f1+tu×d1×fp+tu×d1×vson(u)fv+tu

我们发现这个式子有后效性,那么当然不能打高斯消元,考虑令 fu=au×f1+bu×fp+cu。分类讨论:对于一片叶子,显然有 au=ku,bu=cu=tu

对于非叶子节点 u,我们考虑拆掉 vson(u)fv=f1×av+fu×bv+cv

fu=ku×f1+tud×fp+tud×vson(u)fv+tu=ku×f1+tud×fp+tud×(f1×av+fu×bv+cv)+tu=(ku+tud×av)×f1+tud×fp+(tud×bv)×fu+(tu+tud×cv)

接下来是点睛之笔:把 fu 项移到等式左边!

fu=(ku+tud×av)×f1+tud×fp+(tud×bv)×fu+(tu+tud×cv)fu(tud×bv)×fu=(ku+tud×av)×f1+tud×fp+(tu+tud×cv)(1tud×bv)×fu=(ku+tud×av)×f1+tud×fp+(tu+tud×cv)

1tud×bv 作为分母移过去,我们终于得到:

au=ku+tud×av1tud×bvbu=tu×d11tud×bvcu=tu+tud×cv1tud×bv

这里的每一项都只和 u,v 有关,可以做树型 DP 了!自底向上更新 au,bu,cu 即可。我们其实并不需要计算 fu

最终的答案是 f1=a1×f1+c1,可化简为 f1=c11a1

什么时候会无解呢?分母是 0 的时候。

  • d=0 根本除不动(?????)
  • 计算过程中 1tud×bv=0,寄。
  • 最终 a1=1,相减又得 0,寄。

那么我们可以写代码了。

code

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef long long LL;
const double eps=1e-9;
template<int N,int M,class T=int> struct graph{
    int head[N+10],nxt[M*2+10],cnt;
    struct edge{
        int u,v;T w;
        edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
    } e[M*2+10];
    graph(){memset(head,cnt=0,sizeof head);}
    edge operator[](int i){return e[i];}
    void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
    void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
int n,deg[10010];
double a[10010],b[10010],c[10010],k[10010],t[10010];
graph<10010,10010> g;
int dfs(int u,int fa){
	if(deg[u]==1&&fa) return a[u]=k[u],b[u]=c[u]=t[u],1;
    //			^~~~ important!
	double sa=0,sb=0,sc=0;
	for(int i=g.head[u];i;i=g.nxt[i]){
		int v=g[i].v; if(v==fa) continue;
		if(!dfs(v,u)) return 0;
		sa+=a[v],sb+=b[v],sc+=c[v];
	}
	double del=t[u]/deg[u],tmp=1-del*sb;
	if(fabs(tmp)<eps) return 0;
	a[u]=(k[u]+del*sa)/tmp;
	b[u]=del/tmp;
	c[u]=(t[u]+del*sc)/tmp;
	return 1;
}
int mian(){
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g.link(u,v),deg[u]++,deg[v]++;
	for(int u=1;u<=n;u++) scanf("%lf%lf",&k[u],&t[u]),k[u]/=1e2,t[u]/=1e2,t[u]=1-t[u]-k[u];
	if(dfs(1,0)&&fabs(1-a[1])>eps) printf("%.6lf\n",c[1]/(1-a[1]));
	else puts("impossible"); 
	return 0;
}
void reset(){
	static int ccf=0;
	printf("Case %d: ",++ccf);
	memset(a,0,sizeof a);
	memset(b,0,sizeof b);
	memset(c,0,sizeof c);
	memset(k,0,sizeof k);
	memset(t,0,sizeof t);
	memset(deg,0,sizeof deg);
	memset(g.head,g.cnt=0,sizeof g.head); 
}
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	for(scanf("%*d");~scanf("%d",&n);reset(),mian());
	return 0;
}
posted @   caijianhong  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示