Atcoder Black Cats Deployment(树 + 并查集)

链接:https://cf17-tournament-round3-open.contest.atcoder.jp/tasks/asaporo2_e

题目大意:给定一颗有n个节点的树,每条边有一个权重c, 对于i,X表示对于所有j!=i,从i到j的路径上最小的c之和,求对于所有的i,X分别等于多少?

分析:将所有边按权重排序从大到小,每次插入一条边,对于这条边两边的两个连通块A、B来说,路径上的最小权重都是c,所以对A中的点全部加c*|B|,对B也一样,即可得到答案。这样复杂度是O(n^2)的,合并点集用的是并查集,加一个add数组,使得i的答案是从i到祖先的路径上所有add之和,然后每次更新都是O(1)的,总复杂度为O(n)。

 1 #include
 2 #include
 3 #include
 4 #include
 5 using namespace std;
 6 const int maxn = 1e5 + 5;
 7 typedef long long ll;
 8 int p[maxn], n;
 9 ll add[maxn], cnt[maxn];
10 struct Edge{
11     int a, b;
12     ll c;
13 }e[maxn];
14 bool Cmp(Edge a, Edge b){return a.c > b.c;}
15 int Find(int x){
16     if(p[x] == x)return x;
17     if(Find(p[x]) == p[x])return p[x];
18     add[x] += add[p[x]];
19     p[x] = p[p[x]];
20     //add[x] -= add[p[x]];
21     return p[x];
22 }
23 void Merge(int a, int b, ll c){
24     int x = Find(a), y = Find(b);
25     add[x] += c * cnt[y];
26     add[y] += c * cnt[x];
27     cnt[x] += cnt[y];
28     add[y] -= add[x];
29     p[y] = x;
30 }
31 int main(){
32 //    freopen("e:\\in.txt","r",stdin);
33     cin>>n;
34     int a, b;
35     ll c;
36     for(int i = 0; i < n - 1; i++)scanf("%d%d%lld", &e[i].a, &e[i].b, &e[i].c);
37     sort(e, e + n - 1, Cmp);
38     for(int i = 1; i <= n; i++)p[i] = i, add[i] = 0, cnt[i] = 1;
39     for(int i = 0; i < n - 1; i++){
40         Merge(e[i].a, e[i].b, e[i].c);
41     }
42 //    for(int i = 1; i <= n; i++){
43 //        cout<44 //    }
45     for(int i = 1; i <= n; i++){
46         Find(i);
47 //        cout<
48         ll ans;
49         if(p[i] == i)ans = add[i];
50         else ans = add[i] + add[p[i]];
51         printf("%lld\n", ans);
52     }
53     return 0;
54 }

 

转载于:https://www.cnblogs.com/7391-KID/p/7955216.html

你可能感兴趣的:(Atcoder Black Cats Deployment(树 + 并查集))