题目大意:给定一棵带边权树,给三份点的集合U1,U2,U3,求0.5*(E(dis(u1,u2))+E(dis(u1,u3))+E(dis(u2,u3))).

即,我们需要维护两份点的所有距离和。显然,如果用lca处理会超时,于是我们考虑使用树形dp进行处理。对每条边,我们考虑删除它的代价为它所链接的两个连通分量的不同颜色的数量的积乘以该边权。那么直接用树形dp维护即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int maxn=(2e5)+5;
struct node{
    int to,w;
};
bool che[4][maxn];
ll cnt[3];
vector<node>g[maxn];
ll siz[4][maxn];
bool vis1[maxn];
void dfs1(int u){
    vis1[u]=1;
    for(int i=1;i<=3;i++){
        if(che[i][u])siz[i][u]=1;
    }
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i].to;
        if(!vis1[v]){
            dfs1(v);
            for(int j=1;j<=3;j++)siz[j][u]+=siz[j][v];
        }
    }
}
bool vis2[maxn];
double ans=0;
void dfs2(int u){
    vis2[u]=1;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i].to;
        int w=g[u][i].w;
        if(!vis2[v]){
            dfs2(v);
            for(int j=1;j<=3;j++){
                for(int k=1;k<=3;k++){
                    if(j!=k)ans+=1.0*siz[j][v]*(cnt[k]-siz[k][v])*w/(cnt[k]*cnt[j]*1.0);
                }
            }
        }
    }
}
int main(){
    int n=read();
    for(int i=1;i<n;i++){
        int u=read();
        int v=read();
        int w=read();
        g[u].push_back(node{v,w});
        g[v].push_back(node{u,w});
    }
    for(int i=1;i<=3;i++){
        ll m=read();
        cnt[i]=m;
        for(int j=1;j<=m;j++){
            ll x=read();
            che[i][x]=1;
        }
    }
    dfs1(1);
    dfs2(1);
    printf("%.11lf\n",ans/2.0);
    return 0;
}

 

posted on 2021-11-23 16:18  雪之下雪乃天下第一  阅读(213)  评论(0)    收藏  举报