C Rencontre CCPC 威海

C Rencontre CCPC 威海

给出一棵树,有三个人,每个人都一个集合,表示他们可能会选的点,当三个人确定选的点a,b, c后,d=min(dis(a,v)+dis(b,v)+dis(c,v)),求d的期望值。

题解:

这个题目可能会选择的点就是 三个 点的LCA的深度最小的那个点。

然后推推题目的第二个样例可以发现就是求每一条边的贡献,然后除以最后三个人可以选择的数量的乘积。

因为选择这个最近的点,所以可以发现三个人走到最短的点经过的边一定只会经过一次,所以其实对于一条边的经过的次数就是三个人可以选择的数量的乘积减去每一个端点的1和2和3的乘积。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e5+10;
typedef long long ll;
int head[maxn],to[maxn<<1],nxt[maxn<<1],cnt,w[maxn<<1];
void add(int u,int v,int c){
	++cnt,to[cnt] = v,nxt[cnt] = head[u],w[cnt] = c,head[u] = cnt;
	++cnt,to[cnt] = u,nxt[cnt] = head[v],w[cnt] = c,head[v] = cnt;
}
ll have[10][maxn],sum;
double dfsans(int u,int pre){
	double ans = 0;
	for(int i=head[u];i;i=nxt[i]){
		int v = to[i];
		if(v == pre) continue;
		ans += dfsans(v,u);
		ll res1 = 1,res2 = 1;
		for(int j=1;j<=3;j++) have[j][u]+=have[j][v];
		for(int j=1;j<=3;j++) res1 = res1 * have[j][v];
		for(int j=1;j<=3;j++) res2 = res2 * (have[j][0]-have[j][v]);
		ans += (sum - res2 - res1)*w[i];
	}
	return ans;
}

int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
	}
	sum = 1;
	for(int i=1;i<=3;i++){
		int m,x;
		scanf("%d",&m);
		sum *= m,have[i][0] = m;
		while(m--){
			scanf("%d",&x);
			have[i][x]++;
		}
	}
	double cur = dfsans(1,0);
	double ans = cur/(1.0*sum);
	printf("%.9f
", ans);
	return 0;
}

原文地址:https://www.cnblogs.com/EchoZQN/p/13899198.html