1. 程式人生 > 實用技巧 >2020ccpc威海 Rencontre

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 */