【NOIP2016提高A组模拟7.17】寻找

题目

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的期望值。

分析

发现,当遍历到某个节点是,以该节点为根的子树一定比它的兄弟早遍历。
如果要求x节点,它的兄弟一定会对它有贡献。
对于一个父亲节点F,它的儿子分别是S1、S2、S3······
假设现在求S1,
因为children[v]的顺序随机排列,所以,它的兄弟在它前面的几率是(dfrac{1}{2})
总贡献
starting_time[S1]=starting_time[F]+(size[S2]+size[S3]+···)/2+1=starting_time[F]+(size[F]-1-size[S1])/2+1

#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
const int maxlongint=2147483647;
const int mo=1000000007;
const int N=100005;
using namespace std;
double f[N];
int size[N],fa[N],next[N*2],last[N*2],to[N*2],n,m,tot;
int bj(int x,int y)
{
	next[++tot]=last[x];
	last[x]=tot;
	to[tot]=y;
}
int dg(int x)
{
	size[x]=1;
	for(int i=last[x];i;i=next[i])
	{
		int j=to[i];
		if(j!=fa[x])
		{
			dg(j);
			size[x]+=size[j];
		}
	}
}
int dg1(int x)
{
	for(int i=last[x];i;i=next[i])
	{
		int j=to[i];
		if(j!=fa[x])
		{
			f[j]=f[x]+double(size[x]-1-size[j])*0.5+1;
			dg1(j);
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&fa[i]);
		bj(fa[i],i);
		bj(i,fa[i]);
	}
	dg(1);
	f[1]=1;
	dg1(1);
	for(int i=1;i<=n;i++)
	{
		printf("%.1lf ",f[i]);
	}
}
原文地址:https://www.cnblogs.com/chen1352/p/9013524.html