2020ccpc威海 Rencontre

太可惜了,难过

真的难受啊

对于树上三个点a,b,c 汇于一个点的最短路和 ans,有dis(a,b) + dis(b,c) + dis(c,a) = ans * 2

知道这个以后就可以用换根dp解决了,假设dp[x][i]表示所有i颜色的节点到x的距离和

具体可以看代码,

注意答案会爆long long 

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 2e5+11;
ll gcd(ll a,ll b){
    return b == 0 ? a:gcd(b,a%b);
}


struct Node{
    int p;
    ll len;
    Node(int a,ll ln):p(a),len(ln){}
};
vector<Node>G[maxn];
void add(int x,int y,ll len){
    G[x].push_back(Node(y,len));
}
ll dp[maxn][5];//i号点到x 的距离和
 
ll cnt[maxn][5];

int dfs(int x,int fa){
    dp[x][1] = dp[x][2] = dp[x][3] = 0;
    int l = G[x].size();
    for(int i=0;i<l;i++){
        int p = G[x][i].p;
        ll len = G[x][i].len;
        if(p == fa) continue;
        dfs(p,x);
        for(int j=1;j<=3;j++){
            cnt[x][j] += cnt[p][j];
            dp[x][j] += dp[p][j] + len * cnt[p][j];
        }
    }
    return 0;
}
int dfs2(int x,int fa){
    int l = G[x].size();
    for(int i=0;i<l;i++){
        int p = G[x][i].p;
        ll len = G[x][i].len;
        if(p == fa) continue;
        for(int j=1;j<=3;j++){
            dp[p][j] += dp[x][j] - dp[p][j] - cnt[p][j]*len + (cnt[x][j] - cnt[p][j])*len;
            cnt[p][j] = cnt[x][j];
        }
        dfs2(p,x);
    }
    return 0;
}
 
double a[4];
int list[maxn][5];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int x,y;
        ll len;
        scanf("%d %d %lld",&x,&y,&len);
        add(x,y,len);
        add(y,x,len);
    }
    ll c = 1;
    for(int i=1;i<=3;i++){
        int k;
        scanf("%d",&k);
        c *= k;
        a[i] = k;
        
        while(k--){
            int x;
            scanf("%d",&x);
            cnt[x][i] = 1;
            list[x][i] = 1;
        }
    }
    
    dfs(1,-1);
    dfs2(1,-1);
    c *= 2;
    
    double ans = 0;
    double aa = 0;
    double bb = 0;
    double cc = 0;
    
    
    for(int i=1;i<=n;i++){
        if(list[i][1]){
            aa += dp[i][2];
        }
        if(list[i][2]){
            bb += dp[i][3];
        }
        if(list[i][3]){
            cc += dp[i][1];
        }
    } 
    
    double cns = aa * a[3] + bb*a[1] + cc * a[2];
    
    printf("%lf
",cns/c);

    return 0;
}

/*
5
1 2 3
1 3 5
2 4 7
2 5 11
2 2 4
2 1 2
2 1 5

*/

  

寻找真正的热爱
原文地址:https://www.cnblogs.com/lesning/p/13881005.html