前言
如果没有提示的话,应该很难吧,即使有了提示我也觉得这是个妙妙题。
接下来是超详细讲解,面对长长的式子,请耐心看完,一定会有收获的!
题目
题目大意:
(T) 组数据。
初始时勇敢牛牛在一个 (n) 个点的树的 (1) 号节点上,如果勇敢牛牛在节点 (i),则:
- 有 (K_i) 的概率被击杀,然后返回 (1) 号节点;
- 有 (E_i) 的概率直接找到出口逃出;
- 等概率地在节点 (i) 的所有边中选择一条走。
询问逃出生天的期望走的边数,如果无法逃出生天,输出 ( t impossible)。
注意:输入给的 (K_i) 与 (E_i) 为百分数,并且保证为整数,且 (E_1=K_1=0)。
(1le Tle 3; 1le n le 10^5; 0le K_i,E_ile100; K_i+E_ile100.)
讲解
由于期望 (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_{fa_i}),如果它可以丢掉或者转换成其它形式,那么问题就迎刃而解了。
我们可以将 (E_{fa_i}) 丢给 (fa_i) 自己处理!我们将转移的东西分一下类,然后重新写一下 (E_i) 的表达式:
我们将 (E_1) 的系数叫做 (A_i),(E_{fa_i}) 的系数叫做 (B_i),常数项叫做 (C_i),有:
然后我们将 (E_{son}) 拆开看看会发生什么:
式子已经长得一行需要分行写了,我们令 (cao=1-ki_i-es_i,woc=frac{(1-ki_i-es_i)}{deg_i}),有:
至此,转移已经全部完毕。
对于 (E_1=A_1 imes E_1+B_1 imes 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
");
else printf("%.6f
",C[1] / (1-A[1]));
}
return 0;
}
后记
以后转移互相包含可以试着分类处理,实在搞不定给下一个状态接着搞。
希望没人看出来不文明用语。