Beauty of tree
题目链接
题目简介
一个有根树,现在有两个人以不同的步长(A和B)分别随机选择树上的一个节点向根的方向遍历这个树,
定义这个树的权重为两个人期望遍历到的所有节点数量之和(如果两个人同时遍历到一个节点,那么这个节点只会计算一次),求这个树的权重。
解题思路
(借鉴了榜上第二名大佬的代码和思路)
- 如果直接暴力计算n*n种情况,test set 1是可以通过的。(实测)
- 但是要想通过 test set 2 ,我们还要考虑一些技巧,如果我们认真研究的话,这道题是和概率论紧密相连的。
- 假设题中两个人为 a b , 他们随机选择节点的过程是独立的,当选择的初始节点确定时,所有遍历到的节点就会确定,因此对于某一个节点,设 a 遍历到这个节点的概率为 P a P_a Pa,b 遍历到这个节点的概率为 P b P_b Pb , 那么这个节点被遍历到的概率便是 P a + P b − P a ∗ P b P_a+P_b-P_a*P_b Pa+Pb−Pa∗Pb ,这样我们只需要计算出每个节点的 P a 和 P b P_a和P_b Pa和Pb
- 计算每个节点的 P a 和 P b P_a和P_b Pa和Pb:由于是随机选择的,所以每个节点被选择的概率是相等的,因此我们设每个节点都会选择一次,用path数组分别记录每个节点被两个人遍历的次数,最后将path除以N即算出每个节点分别被两个人遍历到的概率。
- path数组的求法(动态规划的思想):从第N个节点向前遍历(不存在编号大的节点是编号小的节点的祖先),每个节点被遍历到的次数等于它前A步(或B步)的节点被遍历到的次数加1
- 找到一个节点前A步(或B步)的节点:用par数组进行倍增查找(原理请查阅代码)
代码
#include<iostream>
#include<iomanip>
#include<vector>
#include<array>
using namespace std;
int T,A[2],N;
double re;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
for(int g=1;g<=T;g++){
re=0;
cin >> N >> A[0] >> A[1];
vector<array<int,2>>path(N+1);
vector<int>deep(N+1);
vector<array<int,20>>par(N+1);
for(int i=2;i<=N;i++){
cin >> par[i][0];
deep[i] = deep[par[i][0]]+1;
for(int l=0;par[i][l];l++){
par[i][l+1] = par[par[i][l]][l];
}
}
for(int i=N;i>=1;i--){
for(int j=0;j<=1;j++){
path[i][j] ++;
if(deep[i]<A[j])continue;
int v = A[j],cur=i;
for(int l=0;v;v>>=1,l++){
if(v&1)cur = par[cur][l];
}
path[cur][j] += path[i][j];
}
double pa = (double)path[i][0]/N;
double pb = (double)path[i][1]/N;
re += pa + pb - pa * pb;
}
cout << "Case #" << g << ": " << fixed << setprecision(12) << re << "\n";
}
}