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\n",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 */
寻找真正的热爱