HDU4424【并查集】

来源:互联网 发布:彩虹跳转源码 编辑:程序博客网 时间:2024/06/11 03:37

实在不会DFS如何维护什么东西。
这种树上操作,也可以拿拿并查集(考虑集合的特性和合并——保持最优)试试~
思路:
对每个点集维护一个点为汇点(不维护具体标号),维护这个点集的最大权值,再维护这个点集的个数(维护这个主要是为了合并集合之用)
先对所有边权排序(降序),然后每次取一条边,边有两个点,对两个点当前所在的集合。我们先考虑两个集合合并是如何操作的,由于当前边为最小值,所以被合并的集合(汇点不在这个集合上)的点个数乘这条边的权值为这个集合的贡献,再加上另外一个集合的最优值,就是这个新集合的最优解和点数。那么我们已经知道如何合并所以我们只要每次比较一下大小就好了啊~然后就那样合并就好了啊~
那么问题来了——这样为什么是最优的啊???
那个……我也不知道

//#pragma comment(linker, "/STACK:102400000,102400000")#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <set>#include <map>#include <string>#include <math.h>#include <stdlib.h>#include <time.h>using namespace std;typedef long long LL;#define mem(a, b) memset(a, b, sizeof(a))const int Maxn = 2e5 + 10;int pre[Maxn], n;LL num[Maxn], dis[Maxn];struct Edge{    int u, v;    LL w;}edge[Maxn];bool cmp(Edge x, Edge y){    return x.w > y.w;}int Find(int x){    int r = x;    while(pre[r] != r) r = pre[r];    int i = x, j;    while(pre[i] != r){        j = pre[i];        pre[i] = r;        i = j;    }    return r;}void Merge(int x, int y, int w){    LL ans1, ans2;    int xx = Find(x);    int yy = Find(y);    if(xx != yy){        ans1 = dis[xx] + num[yy] * w;        ans2 = dis[yy] + num[xx] * w;        if(ans1 > ans2){            dis[xx] = ans1;            num[xx] = num[xx] + num[yy];            pre[yy] = xx;        }        else{            dis[yy] = ans2;            num[yy] = num[xx] + num[yy];            pre[xx] = yy;        }    }}void solve(){    for(int i=0;i<n-1;i++)        Merge(edge[i].u, edge[i].v, edge[i].w);    printf("%lld\n", dis[Find(1)]);}int main(){    while(~scanf("%d", &n)){        for(int i=0;i<n-1;i++)            scanf("%d%d%lld", &edge[i].u, &edge[i].v, &edge[i].w);        sort(edge, edge+n-1, cmp);        for(int i=1;i<=n;i++){            pre[i] = i;            dis[i] = 0;            num[i] = 1;        }        solve();    }    return 0;}