2020CCPC [Weihai Site]-C. Rencontre(树,贡献)
题面:
题意:
给定一个含有(mathit n)个节点的树,每一个边都有其边权,现在有三个科学家,每一个科学家都有一个节点列表,代表他今晚会从列表中等概率的选择一个节点住下。每个科学家的选择相互独立,都是等概率的。
第二天三个科学家会选择一个节点使其他们到该点的路径权值总和最小,现在让你编程求出科学家们到该点的路径权值总和的期望值。
思路:
显然一共有(m_1*m_2*m_3)种住宿方案。
直接暴力求解显然会超时,那么我们不妨考虑每一个边对期望值的贡献。
我们可以推出如下结论:一个边在某种方案中被走过才对期望有贡献,而一个边在某种方案中最多只会被一个科学家走,且该边如果必须被走时当且仅当:删除该边生成的两个子图中均有科学家住宿。
那么考虑下计算删除该边(e_i)生成的两个子图中均有科学家住宿的方案数为左右科学家123可选节点个数组合计算一下。
代码:
#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+10;
int n;
ll m[3];
vector<pii>g[N];
vector<int>p[N];
ll sz[N][3];
double ans,ret;
void dfs(int u,int fa){
for(int x:p[u]) sz[u][x]=1;
for(pii x:g[u]){
if(x.fi==fa) continue;
dfs(x.fi,u);
for(int i=0;i<3;i++) sz[u][i]+=sz[x.fi][i];
ans+=sz[x.fi][0]*sz[x.fi][1]*(m[2]-sz[x.fi][2])*x.se/ret;
ans+=sz[x.fi][0]*sz[x.fi][2]*(m[1]-sz[x.fi][1])*x.se/ret;
ans+=sz[x.fi][1]*sz[x.fi][2]*(m[0]-sz[x.fi][0])*x.se/ret;
ans+=(m[0]-sz[x.fi][0])*(m[1]-sz[x.fi][1])*sz[x.fi][2]*x.se/ret;
ans+=(m[0]-sz[x.fi][0])*(m[2]-sz[x.fi][2])*sz[x.fi][1]*x.se/ret;
ans+=(m[1]-sz[x.fi][1])*(m[2]-sz[x.fi][2])*sz[x.fi][0]*x.se/ret;
}
}
int main()
{
scanf("%d",&n);
for(int i=2,x,y,w;i<=n;i++){
scanf("%d%d%d",&x,&y,&w);
g[x].push_back(pii(y,w));
g[y].push_back(pii(x,w));
}
for(int i=0;i<3;i++){
scanf("%d",&m[i]);
for(int j=1,x;j<=m[i];j++){
scanf("%d",&x);
p[x].push_back(i);
}
}
ret=(m[0]*m[1]*m[2]);
dfs(1,0);
printf("%.6f
",ans);
return 0;
}