hdu 4424 并查集+分治/贪心 有些最大生成树的感觉

来源:互联网 发布:程序员的梦工厂 编辑:程序博客网 时间:2024/06/18 06:12

题目:

Conquer a New Region

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2086    Accepted Submission(s): 734


Problem Description
The wheel of the history rolling forward, our king conquered a new region in a distant continent.
There are N towns (numbered from 1 to N) in this region connected by several roads. It's confirmed that there is exact one route between any two towns. Traffic is important while controlled colonies are far away from the local country. We define the capacity C(i, j) of a road indicating it is allowed to transport at most C(i, j) goods between town i and town j if there is a road between them. And for a route between i and j, we define a value S(i, j) indicating the maximum traffic capacity between i and j which is equal to the minimum capacity of the roads on the route. 
Our king wants to select a center town to restore his war-resources in which the total traffic capacities from the center to the other N - 1 towns is maximized. Now, you, the best programmer in the kingdom, should help our king to select this center.
 

Input
There are multiple test cases.
The first line of each case contains an integer N. (1 <= N <= 200,000)
The next N - 1 lines each contains three integers a, b, c indicating there is a road between town a and town b whose capacity is c. (1 <= a, b <= N, 1 <= c <= 100,000)
 

Output
For each test case, output an integer indicating the total traffic capacity of the chosen center town.
 

Sample Input
41 2 22 4 12 3 141 2 12 4 12 3 1
 

Sample Output
43
 

Source
2012 Asia ChangChun Regional Contest


给一棵树 要求选一个点,使得其他所有点到该点的路径上所经过边的最小值的和最大 

分析:

暴力模拟会T。一开始以为是要用树的直径(大佬们想的都是树形DP...) 中心点应该是在直径上 但是并不能确定这个点,如果整棵树本来就是一个链,极端情况有200000个点,这种情况就不知道该怎么处理了 事实上从找中心点开始不好切入,可以从边的角度考虑。最终目的相当于从给出的N-1条边中按照某种顺序加边生成一棵树。既然要求和最大,那优先考虑把大的边加进去 加进来这条边之后对最终结果的影响是什么样的呢?

http://www.cnblogs.com/wangsouc/articles/3302556.html

上面链接的思路比较清晰 

考虑到d(u,v)的值是路径上最小的边权值,那么整个图中边权最小的边一定是一个关键边。选出边权最小的边,可以把树分成两个子树,答案在左子树或者右子树上。并且左右子树上的边权都大于选出的边权,那么假设答案在左子树,那右子树到答案点的容量和就是右子树的点数*当前这个最小的边权。最终答案再加上左子树的答案。
同理,如果在右子树也如此。进行比较即可。
这样就把问题按照边分治了。
合并的时候比较左右子树分别作为答案的大小进行合并即可。
分治考虑对边权排序,依次合并,维护一些树,使用并查集即可。


好像挺有道理的样子...尽管并不能证明

随着新边的加入,中心点的位置可能不断变化orz 不断更新就是了...有个贪心的思想....感觉还是没有完全参悟透彻


代码:

#include<iostream>#include<cstdio>#include<cstring>#include<math.h>#include<algorithm>using namespace std;#define ll long longconst ll maxn=2e5+50;ll n,fa[maxn],cnt[maxn],sum[maxn];//父结点编号 该点所在集合中的点数 该点所在集合的S值之和struct edge{    ll f,to,w;}e[maxn];bool cmp(const edge & a, const edge & b){    return a.w>b.w;}ll find(ll x){    if(x==fa[x]) return x;    else return fa[x]=find(fa[x]);}int main(){//998MS10988K    while(scanf("%lld",&n)==1){        for(ll i=1;i<n;++i){            scanf("%lld%lld%lld",&e[i].f,&e[i].to,&e[i].w);            fa[i]=i; cnt[i]=1; sum[i]=0;        }        fa[n]=n; cnt[n]=1; sum[n]=0;        sort(e+1,e+n,cmp);//n-1 条        //long long ans=0;        for(ll i=1;i<n;++i){            edge te=e[i];//下面判断这条边该怎么加            ll l=e[i].f,r=e[i].to;            ll a=find(l),b=find(r);            if(sum[a]+e[i].w*cnt[b]>=sum[b]+e[i].w*cnt[a]){//中心点放在当前边的左侧更好                fa[b]=fa[a];                cnt[a]+=cnt[b];                sum[a]+=e[i].w*cnt[b];            }            else{                fa[a]=fa[b];                cnt[b]+=cnt[a];                sum[b]+=e[i].w*cnt[a];            }        }        printf("%lld\n",sum[find(2)]);//根节点是确定的    }return 0;}



原创粉丝点击