【NOI2012】迷失游乐园【概率期望】【换根dp】【基环树】

传送门

题意:给一棵 n n n个点的带边权树或基环树,随机选一个点作为起点,每次随机走到一个相邻未走过的位置,直到无路可走。求期望路径长度。

n ≤ 1 0 5 n \leq 10^5 n105,为基环树时环的大小不超过 20 20 20

先考虑树怎么做废话

先只考虑从根往下走

显然就是

f u = ∑ v ∈ s o n ( u ) w ( u , v ) + f ( v ) ∣ s o n ( u ) ∣ f_u={\sum_{v\in son(u)}w(u,v)+f(v) \over|son(u)|} fu=son(u)vson(u)w(u,v)+f(v)

考虑其他位置

再考虑强制第一步往上走,然后强行不走回来的期望为 g g g

方程见代码

对于基环树, f f f不受影响。

对于环上的点两个方向带着走到这里的概率和距离转一圈,算出 g g g,树上点方程不变。

然后就可以了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#define MAXN 100005
#define MAXM 200005
using namespace std;
struct edge{int u,v,w;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
inline int read()
{
     int ans=0;
     char c=getchar();
     while (!isdigit(c)) c=getchar();
     while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
     return ans;
}
void addnode(int u,int v,int w)
{
     e[++cnt]=(edge){u,v,w};
     nxt[cnt]=head[u];
     head[u]=cnt;
}
int n,m,son[MAXN];
double F[MAXN],G[MAXN];
namespace Tree
{
     void dfs(int u,int f)
     {
	  for (int i=head[u];i;i=nxt[i])
	       if (e[i].v!=f)
	       {
		    ++son[u];
		    dfs(e[i].v,u);
		    F[u]+=F[e[i].v]+e[i].w;
	       }
	  if (son[u]) F[u]/=son[u];
     }
     void DFS(int v,int f)
     {
	  int u=e[f].u;
	  G[v]=e[f].w+((u==1&&son[u]==1)? 0:((G[u]+F[u]*son[u]-F[v]-e[f].w)/(son[u]-(u==1))));
	  for (int i=head[v];i;i=nxt[i])
	       if (e[i].v!=u)
		    DFS(e[i].v,i);
     }
     void solve()
     {
	  dfs(1,0);
	  for (int i=head[1];i;i=nxt[i]) DFS(e[i].v,i);
	  double ans=F[1];
	  for (int i=2;i<=n;i++) ans+=G[i]/(son[i]+1)+F[i]*son[i]/(son[i]+1);
	  printf("%.5f",ans/n);
     }
}
namespace Circle
{
     bool vis[MAXN],isrt[MAXN];
     int rt[MAXN],w[MAXN],tot,tmp;
     void findroot(int u,int f)
     {
	  vis[u]=true;
	  for (int i=head[u];i;i=nxt[i])
	       if (!vis[e[i].v]&&e[i].v!=rt[1])
	       {
		    findroot(e[i].v,u);
		    if (isrt[e[i].v]&&e[i].v!=tmp) isrt[rt[++tot]=u]=true,w[tot]=e[i].w;
	       }
	       else
		    if (e[i].v!=f&&e[i].v!=rt[1])
			 isrt[rt[tot=1]=u]=true,w[1]=e[i].w,tmp=e[i].v;
	  vis[u]=false;
     }
     void DFS(int v,int f)
     {
	  int u=e[f].u;
	  if (isrt[u]) G[v]=e[f].w+2.0/(son[u]+1)*G[u]+(F[u]*son[u]-F[v]-e[f].w)/(son[u]+1);
	  else G[v]=e[f].w+(G[u]+F[u]*son[u]-F[v]-e[f].w)/son[u];
	  for (int i=head[v];i;i=nxt[i])
	       if (e[i].v!=u)
		    DFS(e[i].v,i);
     }
     void solve()
     {
	  findroot(1,0);
	  for (int t=1;t<=tot;t++)
	  {
	       int u=rt[t];
	       for (int i=head[u];i;i=nxt[i])
		    if (!isrt[e[i].v])
			 ++son[u],Tree::dfs(e[i].v,u),F[u]+=F[e[i].v]+e[i].w;
	       if (son[u]) F[u]/=son[u];
	  }
	  for (int i=1;i<=tot;i++)
	  {
	       double p=0.5;
	       int dis=0;
	       for (int j=i%tot+1;j!=i;j=j%tot+1)
	       {
		    dis+=w[j];
		    if (j%tot+1==i) G[rt[i]]+=p*(dis+F[rt[j]]);
		    else G[rt[i]]+=p*son[rt[j]]/(son[rt[j]]+1)*(dis+F[rt[j]]);
		    p*=1.0/(son[rt[j]]+1);
	       }
	       p=0.5,dis=w[i];
	       for (int j=(i==1? tot:i-1);j!=i;j=(j==1? tot:j-1))
	       {
		    if (i%tot+1==j) G[rt[i]]+=p*(dis+F[rt[j]]);
		    else  G[rt[i]]+=p*son[rt[j]]/(son[rt[j]]+1)*(dis+F[rt[j]]);
		    p*=1.0/(son[rt[j]]+1);dis+=w[j];
	       }
	  }
	  for (int t=1;t<=tot;t++)
	  {
	       int u=rt[t];
	       for (int i=head[u];i;i=nxt[i])
		    if (!isrt[e[i].v])
			 DFS(e[i].v,i);
	  }
	  double ans=0;
	  for (int i=1;i<=n;i++) ans+=(isrt[i]+1)*G[i]/(son[i]+isrt[i]+1)+F[i]*son[i]/(son[i]+isrt[i]+1);
	  printf("%.5f",ans/n);	  
     }
}
int main()
{
     n=read(),m=read();
     for (int i=1;i<=m;i++)
     {
	  int u,v,w;
	  u=read(),v=read(),w=read();
	  addnode(u,v,w);addnode(v,u,w);
     }
     if (m==n-1) Tree::solve();
     else Circle::solve();
     return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值