题目传送门(内部题57)
输入格式
第一行包含一个数:$n$表示树的节点数。
接下来$n-1$行,每行包含两个数:$u,v$表示无根树的一条边。
输出格式
输出$n$行,第$i$行包含一个浮点数,保留三位小数,表示第$i$号点第一次访问的期望时间。
样例
样例输入:
3
1 2
2 3
样例输出:
1.000
2.000
5.000
数据范围与提示
样例解释:
样例解释:容易分析出,所有可能情况下,到达$1$号点和$2$号点的时间都分别是:$1$和$2$,我们考虑$3$号点的到达时间,所有可能的过程:$12(12)*3$,表示先到$1$号店,再到$2$号点,然后重复任意次$1$、$2$(可以是$0$次),最后到达$3$。
对于$12(12)^i3$这个具体过程来说(表示中间经过$i$次$1$、$2$),到达$3$号点的时间是$t_i=2(i+1)+1$,这个随机过程的概率是$p_i={(\frac{1}{2})}^{(i+1)}$,期望的时间是$E(u=3)=\sum \limits_{i=0}^{\infty}t_ip_i=5$,故到达$3$号点的期望时刻为$5$。
数据范围:
对于$10\%$的数据,$1\leqslant 10$,保证每个点的度不超过$2$;
对于另外$20\%$的数据,$1\leqslant n\leqslant {10}^5$,保证每个点的度不超过$2$;
对于另外$20\%$的数据,$1\leqslant n\leqslant 100$;
对于$100\%$的数据,$1\leqslant n\leqslant {10}^5$。
题解
考虑$DP$,设$dp[i]$表示到达$i$点的期望时间。
你可能会很容易的推出来一个式子:
$$dp[u]=dp[fa]+2(n-size[u])-1$$
然后你会发现没有小数,删掉它交暴力。
这就是我的考试全过程……
然而,三位小数就是逗我玩的……
无语……
下面讲一下推导:
考虑一个随机过程,第一次走到$u$号点的时间可以分成两部分,第一部分是从$1$号点随机游走第一次走到$u$的父亲$p$的时间,第二部分是从$p$开始走,第一次走到$u$的时间,由期望的线性性,第一次走到$u$的时间期望等于这两部分期望的和。第一部分是一个子问题,我们考虑怎么解决第二部分,我们把这个问题变成一棵树(并且根节点脑袋上也有一条边),从根节点开始随机游走,走出这棵树期望的时间,我们用$x_u$表示这个期望,我们对$u$的子树中的点也类似地定义$x_v$,这样我们可以列出关系式:
$$x_u=\frac{(1+\sum \limits_{v}(x_u+x_v+1))}{d}$$
其中$d$是$u$的度数(包括那根天线),这个关系是中的第一个$1$表示直接向上走,后面那个扩后中的三部分,那个$1$表示从$u$走向$v$,$x_v$表示从$v$走回来期望时间, 表示这个时候继续走,走出去还需要花的时间。因为是等概率,所以直接乘以$frac{1}{d}$这个概率即可。化简后是:
$$x_u=d+\sum \limits_{v}x_v$$
即$x_u$等于$u$这棵子树的所有节点度的和,考虑到除了那根天线之外,所有的边对度的贡献为$2$,所以:
$$x_u=2size[u]+1$$
这样,子问题就有了一个简单的答案了。我们回到原问题,用$dp[u]$表示第一次走到$u$的期望时间,用$fa$表示$u$的父亲,有:
$$dp[u]=dp[fa]+2(n-size[u])-1$$
时间复杂度:$\Theta(n)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h> using namespace std; struct rec{int nxt,to;}e[200001]; int head[100001],cnt; int n; bool vis[100001]; int size[100001]; long long dp[100001]; void add(int x,int y) { e[++cnt].nxt=head[x]; e[cnt].to=y; head[x]=cnt; } void dfs1(int x) { vis[x]=1; size[x]=1; for(int i=head[x];i;i=e[i].nxt) if(!vis[e[i].to]) { dfs1(e[i].to); size[x]+=size[e[i].to]; } } void dfs2(int x) { vis[x]=1; for(int i=head[x];i;i=e[i].nxt) if(!vis[e[i].to]) { dp[e[i].to]=dp[x]+2*(n-size[e[i].to])-1; dfs2(e[i].to); } } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); add(x,y);add(y,x); } dfs1(1); memset(vis,0,sizeof(vis)); dp[1]=1; dfs2(1); for(int i=1;i<=n;i++) printf("%.3lf\n",(double)dp[i]); return 0; }
rp++
原文地址:https://www.cnblogs.com/wzc521/p/11586404.html