【JZOJ4647】寻找

本文介绍了一种在树形结构中计算每个节点DFS遍历起始时间期望值的方法。通过分析节点间的关系,得出每个节点对其兄弟节点期望步数值的贡献公式,并通过递归深度优先搜索算法实现计算。文章提供了完整的代码实现。

description

Bob和Alice出去度蜜月,但Alice不慎走失,Bob在伤心过后,决定前去寻找Alice。
他们度蜜月的地方是一棵树,共有N个节点,Bob会使用下列DFS算法对该树进行遍历。

    starting_time是一个容量为n的数组
    current_time = 0
    dfs(v):        
           current_time = current_time + 1        
           starting_time[v] = current_time        
           将children[v]的顺序随机排列 (每个排列的概率相同)        
           // children[v]v的直接儿子组成的数组        
           for u in children[v]:               
                   dfs(u)

1是这棵树的根,Bob会从1出发,即运行dfs(1),现在他想知道每个点starting_time的期望值。


analysis

  • 观察样例,可知对于一个节点的任意两个儿子节点,一个在另一个前面的概率总是121\over 221

  • 也就是说每个节点对任意一个兄弟节点的期望步数值贡献为该节点子树大小/2/2/2

  • 所以f[i]=f[fa]+∑x∈son&x≠isize[x]2+12f[i]=f[fa]+\sum_{x\in son \&x≠i}{size[x]\over 2}+{1\over2}f[i]=f[fa]+xson&x̸=i2size[x]+21,整理得f[i]=f[fa]+size[fa]−size[i]+12f[i]=f[fa]+{{size[fa]-size[i]+1}\over 2}f[i]=f[fa]+2size[fa]size[i]+1


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 100005
#define MAXM MAXN<<1
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
#define rep(i,a) for (reg i=last[a];i;i=next[i])
#define O3 __attribute__((optimize("-O3")))

using namespace std;

ll last[MAXM],next[MAXM],tov[MAXM],size[MAXN];
double ans[MAXN];
ll n,tot;

O3 inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
O3 inline void link(ll x,ll y)
{
	next[++tot]=last[x],last[x]=tot,tov[tot]=y;
}
O3 inline void dfs(ll x)
{
	size[x]=1;rep(i,x)dfs(tov[i]),size[x]+=size[tov[i]];
}
O3 inline void dfs1(ll x)
{
	rep(i,x)ans[tov[i]]=ans[x]+(size[x]-size[tov[i]]+1)/2.0,dfs1(tov[i]);
}
O3 int main()
{
	freopen("T1.in","r",stdin);
	n=read();
	fo(i,2,n){ll x=read();link(x,i);}
	dfs(1),ans[1]=1.0,dfs1(1);
	fo(i,1,n)printf("%.1lf ",ans[i]);
	printf("\n");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值