CF697D-Puzzles

题目

一棵树,从根节点开始dfs,每层以随机顺序进入每个子节点,问走到每个点的时候期望经过了多少个点。

(这里经过多少个点指的是经过多少个不同的点,即经过一个点多次算一个)

(其实这个题不如说求期望dfn序)。

\(n\le 10^5\)

分析

一个很明显的思路就是:\(f[x]=1+f[fa]+绕来绕去期望经过的点个数\) 。从上往下dfs,问题就转化成了如何求每一个点\(x\)进入其子节点\(v\)之前期望经过的点个数。设绕来

绕去期望经过点个数为\(g[x]\) ,它的父亲有\(n\)个子节点。

计算这个东西有两种思路:

思路1

注意到这是一个dfs,所以我们如果进了一颗子树,那么它会走完整个子树再出来,而这个子树之前是没有走过的,即点数增加了\(\text{size}[v]\) 。(\(v\)为与\(x\)同父亲的点)

这样我们就可以通过枚举之前走进了多少个子树来求:

\[\begin{aligned} g[x]&=\sum _{i=1}^{n-1} 任意不含x的i个的size的和*\frac{1}{n}*\frac{1}{n-1}*\cdots*\frac{1}{n-i} \\ &=\sum _{i=1}^{n-1} \frac{(n-i-1)!}{n!}\sum _{v\ne x}i*size[v]* A_{n-2}^{i-1} \\ &=\sum _{i=1}^{n-1} \frac{(n-i-1)!}{n!}\sum _{v\ne x}i*size[v]* \frac{(n-2)!}{(n-i-1)!} \\ &=\sum _{v\ne x}size[v] \sum _{i=1}^{n-1}\frac{(n-2)!}{n!} \\ &=\frac{1}{2}\sum _{v\ne x}size[v] \end{aligned} \]

思路2

把从\(x\)父亲进入这一层的顺序列出来,所有情况是它的全排列。\(x\)前面有\(i\)个数的概率为\(\frac{1}{n}\), 前面\(i\)个数的和的期望为\(\frac{i\sum _{v\ne x}size[v]}{n-1}\),所以所有情况为

\[\begin{aligned} g[x]&=\frac{1}{n}*\frac{\sum _{i=1}^{n-1}i\sum _{v\ne x}size[v]}{n-1} \\ &=\frac{1}{2}\sum _{v\ne x}size[v] \end{aligned} \]

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int read() {
	int x=0,f=1;
	char c=getchar();
	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
	return x*f;
}
const int maxn=1e5+1;
vector<int> g[maxn];
double f[maxn];
int size[maxn];
inline void add(int x,int y) {g[x].push_back(y);}
int Size(int x) {
	int &sz=size[x]=1;
	for (int v:g[x]) sz+=Size(v);
	return sz;
}
void dfs(int x) {
	for (int v:g[x]) f[v]=1.0+f[x]+(double)(size[x]-size[v]-1)/2.0,dfs(v);
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
#endif
	int n=read();
	for (int i=2;i<=n;++i) add(read(),i);
	f[1]=1;
	Size(1);
	dfs(1);
	for (int i=1;i<=n;++i) printf("%.2lf%c",f[i]," \n"[i==n]);
	return 0;
}
posted @ 2017-07-09 22:20  permui  阅读(471)  评论(0编辑  收藏  举报