[HDU4035] Maze

前言

如果没有提示的话,应该很难吧,即使有了提示我也觉得这是个妙妙题。

接下来是超详细讲解,面对长长的式子,请耐心看完,一定会有收获的!

题目

HDU

VJudge

题目大意:

\(T\) 组数据。

初始时勇敢牛牛在一个 \(n\) 个点的树的 \(1\) 号节点上,如果勇敢牛牛在节点 \(i\),则:

  • \(K_i\) 的概率被击杀,然后返回 \(1\) 号节点;
  • \(E_i\) 的概率直接找到出口逃出;
  • 等概率地在节点 \(i\) 的所有边中选择一条走。

询问逃出生天的期望走的边数,如果无法逃出生天,输出 \(\tt impossible\)

注意:输入给的 \(K_i\)\(E_i\) 为百分数,并且保证为整数,且 \(E_1=K_1=0\)

\(1\le T\le 3;\ 1\le n \le 10^5;\ 0\le K_i,E_i\le100;\ K_i+E_i\le100.\)

讲解

由于期望 \(E\) 和题目中的概率 \(E_i\) 重复了,所以我们把 \(E_i,K_i\) 变为 \(es_i,ki_i(escape\ \&\ kill)\),而且这样和代码中的定义相同。

如果没看到数据范围,你可能会天真地想高斯消元,可现实是残酷的,但你也得勇敢面对。

勇敢牛牛,不怕困难!我们以 \(1\) 为根,直接把式子列出来树形DP!

\(E_i\) 表示从 \(i\) 为起点逃生的期望边数,\(deg_i\)\(i\) 节点的度数,\(fa_i\)\(i\) 节点的父亲,则有:

\[E_i=ki_i\times E_1+es_i\times 0+\frac{(1-ki_i-es_i)}{deg_i}\times(E_{fa_i}+1+\sum (E_{son}+1)) \]

可以发现,最难处理的是 \(E_{fa_i}\),如果它可以丢掉或者转换成其它形式,那么问题就迎刃而解了。

我们可以将 \(E_{fa_i}\) 丢给 \(fa_i\) 自己处理!我们将转移的东西分一下类,然后重新写一下 \(E_i\) 的表达式:

\[\begin{aligned} E_i&=ki_i\times E_1+\frac{(1-ki_i-es_i)}{deg_i}\times(E_{fa_i}+1+\sum (E_{son}+1))\\ &=ki_i\times E_1+\frac{(1-ki_i-es_i)}{deg_i}\times E_{fa_i}+\frac{(1-ki_i-es_i)}{deg_i}\times(\sum E_{son}+deg_x)\\ &=ki_i\times E_1+\frac{(1-ki_i-es_i)}{deg_i}\times E_{fa_i}+\frac{(1-ki_i-es_i)}{deg_i}\times\sum E_{son}+(1-ki_i-es_i)\\ \end{aligned} \]

我们将 \(E_1\) 的系数叫做 \(A_i\)\(E_{fa_i}\) 的系数叫做 \(B_i\),常数项叫做 \(C_i\),有:

\[\begin{aligned} &E_i=A_i\times E_1+B_i\times E_{fa_i}+C_i\\ &A_i=ki_i\\ &B_i=\frac{(1-ki_i-es_i)}{deg_i}\\ &C_i=\frac{(1-ki_i-es_i)}{deg_i}\times\sum E_{son}+(1-ki_i-es_i)\\ \end{aligned} \]

然后我们将 \(E_{son}\) 拆开看看会发生什么:

\[\begin{aligned} E_i&=A_i\times E_1+B_i\times E_{fa_i}+C_i\\ &=A_i\times E_1+B_i\times E_{fa_i}+(1-ki_i-es_i)\\ &\ \ \ \ \ +\frac{(1-ki_i-es_i)}{deg_i}\times\sum (A_{son}\times E_1+B_{son}\times{E_i}+C_{son})\\ (1-\frac{(1-ki_i-es_i)}{deg_i}\sum B_{son})E_i&=(A_i+\frac{(1-ki_i-es_i)}{deg_i}\sum A_{son})\times E_1+B_i\times E_{fa_i}\\ &\ \ \ \ \ +\frac{(1-ki_i-es_i)}{deg_i}\sum C_{son}+(1-ki_i-es_i)\\ \end{aligned} \]

式子已经长得一行需要分行写了,我们令 \(cao=1-ki_i-es_i,woc=\frac{(1-ki_i-es_i)}{deg_i}\),有:

\[\begin{aligned} (1-woc\sum B_{son})E_i&=(A_i+woc\sum A_{son})\times E_1+B_i\times E_{fa_i}+woc\sum C_{son}+cao\\ E_i&=\frac{A_i+woc\sum A_{son}}{1-woc\sum B_{son}}\times E_1+\frac{B_i}{1-woc\sum B_{son}}\times E_{fa_i}+\frac{woc\sum C_{son}+cao}{1-woc\sum B_{son}}\\ \end{aligned} \]

至此,转移已经全部完毕。

对于 \(E_1=A_1\times E_1+B_1\times E_{fa_1}+C_1(E_{fa_1}=0)\),易得 \(E_1=\frac{C_1}{1-A_1}\)

如果 \(A_1\) 趋近于 \(1\) 则永远被困,否则输出答案即可。

代码

注意多组清空。

点击查看
//12252024832524
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 100005;
const double eps = 1e-10;
int n,cas,deg[MAXN];
double ki[MAXN],es[MAXN],A[MAXN],B[MAXN],C[MAXN];

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int head[MAXN],tot;
struct edge
{
	int v,nxt;
}e[MAXN << 1];
void Add_Edge(int x,int y)
{
	e[++tot].v = y;
	e[tot].nxt = head[x];
	head[x] = tot;
	deg[x]++;
}
void Add_Double_Edge(int x,int y)
{
	Add_Edge(x,y);
	Add_Edge(y,x);
}
void dfs(int x,int fa)
{
	double cao = 1-ki[x]-es[x],cs = 1,woc = (1-ki[x]-es[x])/deg[x];
	A[x] = ki[x]; B[x] = woc; C[x] = cao;
	for(int i = head[x],v; i ;i = e[i].nxt)
	{
		v = e[i].v;
		if(v == fa) continue;
		dfs(v,x);
		A[x] += woc*A[v];
		C[x] += woc*C[v];
		cs -= woc*B[v];
	}
	A[x] /= cs; B[x] /= cs; C[x] /= cs;
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	for(int T = Read(); T ;-- T)
	{
		n = Read(); tot = 0;
		for(int i = 1;i <= n;++ i) deg[i] = head[i] = 0,A[i] = B[i] = C[i] = 0;
		for(int i = 1;i < n;++ i) Add_Double_Edge(Read(),Read());
		for(int i = 1;i <= n;++ i) ki[i] = Read() / 100.0,es[i] = Read() / 100.0;
		printf("Case %d: ",++cas);
		dfs(1,0);
		if(Abs(1-A[1]) < eps) printf("impossible\n");
		else printf("%.6f\n",C[1] / (1-A[1]));
	}
	return 0;
}

后记

以后转移互相包含可以试着分类处理,实在搞不定给下一个状态接着搞。

希望没人看出来不文明用语。

posted @ 2021-07-22 21:29  皮皮刘  阅读(77)  评论(0编辑  收藏  举报