hdu4424 Conquer a New Region

记录下自己的思路。。

题意大概就是N个点N-1条边,两个点之间的运输量记录为两点路径上边权的最小值,求从哪一点出发到剩下点得运输量和最大,输出最大的运输量总和。

首先很明显这些点构成一棵树,那么每一条边都可以把原树分成两个部分。可以把最开始的树看成一个集合,在把所有边从小到大排序,那么当前插入的边可以把所在的集合分成两个集合,而两个集合中的点的运输总量分别增加插入边权值*对方的集合点数。相当于是把一个大集合不断分割的过程,最后可以得到每个点的运输总量。但是在实现过程当中发现集合的分割不好实现。

这时候想到反其道行之,将边从大到小排序再插入,那么每个点刚开始都是自成一个集合,然后不断合并,就是一个并查集。这里要用到两个并查集,一个是用来记录合并过程当中已合并的集合,另一个则是用来记录运输量的增量。当然,别人有比我思路更简洁的方法,这只是记录一下我在当时的思路而已。

</pre><p></p><pre name="code" class="cpp"><pre name="code" class="cpp">#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int pre[401000],n,cnt,no[401000],siz[401000],p[401000];
long long addv[401000];
typedef struct node node;
struct node
{	
	int from,to,w;
}edge[201000];
int find_ds(int a){
	return a==pre[a]?a:pre[a]=find_ds(pre[a]);
}
int find_ps(int a){
	return a==p[a]?a:p[a]=find_ps(p[a]);
}
long long find_p(int a){
	if(p[a]==a){
		return addv[a];
	}
	else{
		addv[a]+=find_p(p[a]);
		find_ps(a);
		return addv[a];
	}
}
int cmp(int a,int b){
	return edge[a].w>edge[b].w;
}
int main(){
	int a,b,c;
	while(scanf("%d",&n)!=EOF){
		for(int i=0;i<n-1;i++){
			scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].w);
		}
		for(int i=1;i<=2*n+10;i++){
			no[i]=i;
			pre[i]=i;
			p[i]=i;
			siz[i]=1;
			addv[i]=0;
		}
		no[0]=0;
		sort(no,no+n-1,cmp);
		int f,t,id;
		int cntt=0;
		for(int i=0;i<n-1;i++){
			id=no[i];
			t=find_ds(edge[id].to);
			f=find_ds(edge[id].from);
			addv[f]+=(long long)edge[id].w*siz[t];
			addv[t]+=(long long)edge[id].w*siz[f];
			cntt++;
			pre[f]=cntt+n;
			pre[t]=cntt+n;
			p[t]=cntt+n;
			p[f]=cntt+n;
			siz[cntt+n]=siz[f]+siz[t];
		}
		long long ans=0;
		for(int i=1;i<=n;i++){
			if(ans<find_p(i)){
				ans=find_p(i);
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值