题意
有一个树形的迷宫,你从根节点1出发,每一个节点有(k_i)的概率死去,并重新从节点1出发;有(e_i)的概率逃脱迷宫,从一个点有相等的概率穿过一次通道到达其他的点,求成功逃脱的期望次数
节点数(nleq1e5)
思路
我们设(f(i))表示从i出发逃脱的期望次数,(deg_i)表示节点i的度
一看就是一个期望题嘛,我们考虑如何树形DP
由于求的时候会先算出子节点,我们将子节点与父节点分开来处理
可以得到
[f(i)=k_if(1)+frac{1-k_i-e_i}{deg_i}sum_{jin 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_{jin 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_isum_{jin son(i)}b_j)f_i=(k_i+D_isum_{jin son(i)}A_j)f(1)+D_if(fa_i)+D_i+D_i(sum_{jin son(i)}(C_j+1))
]
令(T_i=1-d_isum_{jin son(i)}b_j)
则
[A_i=frac{k_i+D_isum_{jin son(i)}A_j}{T_i}
]
[B_i=frac{D_i}{T_i}
]
[C_i=frac{D_i+D_i(sum_{jin son(i)}(C_j+1))}{T_i}=frac{D_i*deg_i+D_i(sum_{jin 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
",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
",fabs(1-B[1]),eps);
if(fabs(1-B[1])<eps || flag){
printf("impossible
");
}else{
printf("%.6lf
",C[1]/(1.0-B[1]));
}
}
return 0;
}
后记
这道题体现出了循环转移方程的线性解法,关键在于有一个核心状态与所有状态有关,才能如此求解