题面
这题面太毒瘤了给n个点,n-1条边,组成一个树,三个人选其中的m1个点,m2个点,m3个点。然后三个人随便选一个地方住下,然后第二天到某一个点集合,是最小路径,求期望路径是多少
第二个样例
5
1 2 3
1 3 5
2 4 7
2 5 11
3 2 4 5
4 1 2 3 5
2 1 3
第二个人和第三个人重复了1,然后模拟比赛的时候翻译说不能同一家,然后样例一直输不对。。。赛后看题解,直接除以(m1 * m2 * m3)即可,不需要找重复……
就这句话直接把我们的翻译看懵了,然后手算样例算不对,笑死。
They decide to stay in separate hotels at night and meet in one hotel the next day.
思路
其实是一道很经典的树上求贡献染色图(?)如果不知道这个可以看一下这篇博客
然后我的思路就是跑一边,然后求每个边做的贡献,把第1,2,3人当作红黄蓝染色
贡献就是:
(红左个数 * 黄右个数 * 蓝右个数 +
黄左个数 * 红右 个数* 蓝右个数 +
蓝左个数 * 黄右个数 * 红右个数 +
红左个数 * 黄左个数 * 蓝右个数 +
黄左个数 * 红右个数 * 蓝左个数 +
蓝左个数 * 黄右个数 * 红左个数)/m1/m2/m3 * 边长
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+9;
int n;
int ne[N<<1],to[N<<1],head[N],dis[N<<1],tot,yans[N][3];
ll sz[N][3];
int k[3];
double ans;
void inint(){
tot=0;memset(head,-1,sizeof(head));
}
void add(int u,int v,int w){
ne[tot]=head[u];
to[tot]=v;
dis[tot]=w;
head[u]=tot++;
}
void dfs(int u,int fa){
for(int i=0;i<3;i++){
sz[u][i]=(ll)yans[u][i];
}
for(int i=head[u];~i;i=ne[i]){
int v=to[i];if(v==fa)continue;
dfs(v,u);
for(int i=0;i<3;i++){
sz[u][i]+=sz[v][i];
}
}
for(int i=head[u];~i;i=ne[i]){
int v=to[i];if(v==fa)continue;
ll sum=0;
sum+=(sz[v][0]*(k[1]-sz[v][1])*(k[2]-sz[v][2]))+(sz[v][1]*(k[0]-sz[v][0])*(k[2]-sz[v][2]));
sum+=(sz[v][2]*(k[1]-sz[v][1])*(k[0]-sz[v][0]))+(sz[v][2]*sz[v][0]*(k[1]-sz[v][1]));
sum+=(sz[v][0]*sz[v][1]*(k[2]-sz[v][2]))+(sz[v][2]*sz[v][1]*(k[0]-sz[v][0]));
// cout<<u<<v<<(sum*dis[i]/(double)k[0]/(double)k[1]/(double)k[2])<<endl;
ans+=((sum*1.0/(double)k[0]/(double)k[1]/(double)k[2])*(double)dis[i]);
}
}
int main(){
inint();
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);add(v,u,w);
}
ans=0.0;
for(int i=0;i<3;i++){
scanf("%d",&k[i]);
for(int j=0;j<k[i];j++){
int num;scanf("%d",&num);
yans[num][i]=1;
}
}
dfs(1,0);
printf("%f
",ans);
return 0;
}
小声哔哔一句,威海这人均4题,太猛了吧。。。模拟赛的时候各种wa飞,磕磕碰碰的4题……C题题面和样例解释不了,G题线段树不会写,但赛后补了一下哈希线段树,tml