kickstart 2020 D Beauty of tree

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+PbPaPb ,这样我们只需要计算出每个节点的 P a 和 P b P_a和P_b PaPb
  • 计算每个节点的 P a 和 P b P_a和P_b PaPb:由于是随机选择的,所以每个节点被选择的概率是相等的,因此我们设每个节点都会选择一次,用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";
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值