BZOJ2878: [Noi2012]迷失游乐园

编程入门 行业动态 更新时间:2024-10-07 11:28:26

BZOJ2878: [Noi2012]迷失<a href=https://www.elefans.com/category/jswz/34/1766806.html style=游乐园"/>

BZOJ2878: [Noi2012]迷失游乐园

BZOJ2878: [Noi2012]迷失游乐园

树形Dp


题解:

orz orz orz orz
ORZ Tunix ORZ
orz orz orz orz


简单写一下,怕自己忘了。。。

先考虑没有环的情况。

令点1为树根,设 down[i] 表示从i开始向下走的期望步数, up[i] 表示往上走的。

down[i] 的计算很简单:

down[i]=∑(down[j]+w[i][j])son[i]

考虑 up[i] ,点i走到父亲f后,可以顺着其他的儿子向下走,也可以继续往上。所以:

up[i]=len[i][f]+down[f]∗son[f]−down[i]−len[f][i]+up[f]son[f]−1+1

最后,以每个点为出发点统计答案:

ans=∑ni=1down[i]∗son[i]+up[i]son[i]+1n


考虑环呢?注意最多只有一个环。

不在环上的点一样算,环上的down也一样算。

环上的点的up值其实就是沿着环走到其他的任意一个环上的点,然后再沿外向树向下走的期望长度。

这一段直接看代码吧。

for(int i=1;i<=tot;i++){int now=cir[i];double k=1;for(int x=nxt[now];x!=now;x=nxt[x]){if(nxt[x]!=now)up[now]+=k*(len[id[pre[x]]][id[x]]+down[x]*son[x]/(son[x]+1));elseup[now]+=k*(len[id[pre[x]]][id[x]]+down[x]);k/=(son[x]+1);}k=1;for(int x=pre[now];x!=now;x=pre[x]){if(pre[x]!=now)up[now]+=k*(len[id[nxt[x]]][id[x]]+down[x]*son[x]/(son[x]+1));elseup[now]+=k*(len[id[nxt[x]]][id[x]]+down[x]);k/=(son[x]+1);}up[now]/=2;
}

两个并列的循环分别是顺时针、逆时针,最后/2。k是走到这个点的概率,每次乘以 1son[x]+1 .

另外环上的点算是有两个父亲,于是定义 pa[i] 表示i的父亲个数,则 up[i] 的式子化为:

up[i]=len[i][f]+down[f]∗son[f]−down[i]−len[f][i]+up[f]∗fa[i]son[f]−1+pa[i]


注意除0!

Code:

#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
using namespace std;
const int N = 100005;int n,m,flag,tot; double up[N],down[N]; bool bo[N];
int len[21][21],cir[21],id[N],pre[N],nxt[N],fa[N],son[N];struct Edge{int to,next,w;
} e[N*2];
int head[N], ec=0;
void add(int a,int b,int w){ec++; e[ec].to=b; e[ec].w=w;e[ec].next=head[a]; head[a]=ec;
}void findcir(int u,int f){bo[u]=true;for(int i=head[u];i;i=e[i].next){int v=e[i].to; if(v==f) continue;if(bo[v]){ flag=v; return; }findcir(v,u);if(flag>0){if(flag==u) flag=-1;return;}if(flag==-1) break;}bo[u]=false;
}void dfscir(int u,int f){if(id[u]) return;id[u]=++tot; cir[tot]=u; fa[u]=2;for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(v==f) continue;if(!bo[v]) continue;nxt[u]=v; pre[v]=u;dfscir(v,u);len[id[u]][id[v]]=len[id[v]][id[u]]=e[i].w;break;}
} void dfsdown(int u,int f){for(int i=head[u];i;i=e[i].next){int v=e[i].to;if(v==f || bo[v]) continue;fa[v]=1; dfsdown(v,u);son[u]++; down[u]+=down[v]+e[i].w;}if(son[u]) down[u]/=son[u];
}void dfsup(int u,int f,int w){up[u]=w;if(son[f]+fa[f]>1){up[u]+=(up[f]*fa[f]+son[f]*down[f]-down[u]-w)/(son[f]+fa[f]-1);}for(int i=head[u];i;i=e[i].next){if(e[i].to!=f) dfsup(e[i].to,u,e[i].w);}
}int main(){freopen("a.in","r",stdin);freopen("a.out","w",stdout);cin>>n>>m; int a,b,w;for(int i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&w);add(a,b,w); add(b,a,w);}if(m<n){dfsdown(1,0);for(int i=head[1];i;i=e[i].next)dfsup(e[i].to,1,e[i].w);}else{findcir(1,0);//      for(int i=1;i<=n;i++) if(bo[i]) D(i); E;for(int i=1;i<=n;i++){if(bo[i]){ dfscir(i,0); break; }}//      for(int i=1;i<=n;i++){ if(bo[i]){ D(i); D(pre[i]); D(nxt[i]); E; } }for(int i=1;i<=tot;i++){ dfsdown(cir[i],0); }for(int i=1;i<=tot;i++){int now=cir[i];double k=1;for(int x=nxt[now];x!=now;x=nxt[x]){if(nxt[x]!=now)up[now]+=k*(len[id[pre[x]]][id[x]]+down[x]*son[x]/(son[x]+1));elseup[now]+=k*(len[id[pre[x]]][id[x]]+down[x]);k/=(son[x]+1);}k=1;for(int x=pre[now];x!=now;x=pre[x]){if(pre[x]!=now)up[now]+=k*(len[id[nxt[x]]][id[x]]+down[x]*son[x]/(son[x]+1));elseup[now]+=k*(len[id[nxt[x]]][id[x]]+down[x]);k/=(son[x]+1);}up[now]/=2;}for(int j=1;j<=tot;j++){for(int i=head[cir[j]];i;i=e[i].next)if(!id[e[i].to]) dfsup(e[i].to,cir[j],e[i].w);}}//  for(int i=1;i<=n;i++) D(down[i]), E;
//  for(int i=1;i<=n;i++) D(up[i]), E;double ans=0;for(int i=1;i<=n;i++) ans+=(up[i]*fa[i]+down[i]*son[i])/(fa[i]+son[i]);printf("%.5f\n",ans/n);
}

更多推荐

BZOJ2878: [Noi2012]迷失游乐园

本文发布于:2024-02-27 22:47:48,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1766698.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:游乐园

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!