题解 P2081 【[NOI2012]迷失游乐园】
xtx1092515503 · · 题解
发现这张图要么是一棵树,要么是一棵基环树。
我们先考虑树的做法。设一个点
考虑二次扫描与换根法。我们设
这种定义自然也能做。但是,为了方便(也是为了与接下来基环树部分的讲解一致),我们统一将
于是,我们现在有转移方程:
其中,
则我们经历过一遍DP后,
我们有
其中,最外面的那个大分数的分子,是删去
最终答案为
其中
时间复杂度
此部分代码:
namespace SUB1{
double f[100100],res;
void dfs1(int x,int fa){
for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa)dfs1(edge[i].to,x),f[x]+=f[edge[i].to]/max(1,deg[edge[i].to]-1)+edge[i].val;
}
void dfs2(int x,int fa){
for(int i=head[x],y;i!=-1;i=edge[i].next)if((y=edge[i].to)!=fa)f[y]+=(f[x]-f[y]/max(1,deg[y]-1)-edge[i].val)/max(1,deg[x]-1)+edge[i].val,dfs2(y,x);
}
void solve(){
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++)res+=f[i]/deg[i];
printf("%lf\n",res/n);
exit(0);
}
}
接下来我们再考虑基环树的情形。
我们考虑找出在环上的节点。然后,以环上每个节点为根,在它所对应的子树内,仍然一次搜索DP出
接下来我们考虑对于环上的节点,求出它真实的答案。
我们设当前考虑的是节点
-
向
i 节点方向的子树中延伸。此时即为f_i 。 -
向
i 节点的子树外延伸。此时,它可以经过环上与i 节点相邻的两条边中任意一条边,且最多只能经过一条边。故我们可以分别断开两条边中的某一条,并求出此时的结果,最终加在一起即可。
我们设
则我们对于环上节点
其中,
需要注意的是,当这个 DP 在环上绕了一圈后,是不能重新回到
-
当
x 的下一个节点是i 时,我们直接有h_x=f_x ; -
当
x 的下一个节点就是(1)中所述节点时,因为它不能走向i ,所以我们有
其它情形的
我们令
我们分别以环上每个节点为
时间复杂度
此部分代码:
namespace SUB2{
double f[100100],g[100100],h[100100],res;
int fa[100100],sz;
bool on[100100];
void dfscir(int x){
for(int i=head[x];i!=-1;i=edge[i].next){
if(edge[i].to==fa[x])continue;
if(!fa[edge[i].to])fa[edge[i].to]=x,dfscir(edge[i].to);
else{
int y=x;
while(y!=edge[i].to)on[y]=true,y=fa[y],sz++;
on[y]=true,sz++;
}
if(sz)break;
}
}
void dfsord(int x){
for(int i=head[x];i!=-1;i=edge[i].next)if(!on[edge[i].to]&&edge[i].to!=fa[x])fa[edge[i].to]=x,dfsord(edge[i].to);
}
void dfs1(int x){
for(int i=head[x];i!=-1;i=edge[i].next)if(!on[edge[i].to]&&edge[i].to!=fa[x])dfs1(edge[i].to),f[x]+=f[edge[i].to]/max(1,deg[edge[i].to]-1)+edge[i].val;
}
void dfsg(int x,int fr,int rem){
h[x]=f[x];
if(rem==1)return;
for(int i=head[x];i!=-1;i=edge[i].next)if(on[edge[i].to]&&edge[i].to!=fr){
dfsg(edge[i].to,x,rem-1);
h[x]+=h[edge[i].to]/max(1,deg[edge[i].to]-1-(rem==2))+edge[i].val;
}
}
void dfss(int x){
for(int i=head[x];i!=-1;i=edge[i].next)if(!on[edge[i].to]&&edge[i].to!=fa[x]){
g[edge[i].to]=(g[x]-f[edge[i].to]/max(1,deg[edge[i].to]-1)-edge[i].val)/max(1,deg[x]-1)+edge[i].val;
g[edge[i].to]+=f[edge[i].to];
dfss(edge[i].to);
}
}
void solve(){
fa[1]=-1,dfscir(1);
for(int i=1;i<=n;i++)if(on[i])fa[i]=0,dfsord(i);
// for(int i=1;i<=n;i++)printf("%d ",fa[i]);puts("");
for(int i=1;i<=n;i++)if(on[i])dfs1(i);
// for(int i=1;i<=n;i++)printf("%lf\n",f[i]);puts("");
for(int i=1;i<=n;i++)if(on[i])dfsg(i,0,sz),g[i]=h[i];
for(int i=1;i<=n;i++)if(on[i])dfss(i);
for(int i=1;i<=n;i++)res+=g[i]/deg[i];
printf("%lf\n",res/n);
}
}