HDU-3938 Portal (并查集+离线输出)

这也是作业啊,可惜题目都看不懂,然后就果断看题解了。

因为考虑到数据的情况,如果输入一次做一次计算,然后输出,10000组数据会TLE呢,所以就看了题解的离线输出。

先在que的结构体中设置一个L,和一个id,先按照L排序,按顺序算出答案,最后再按照初始的id排序输出答案。

题目给了很多边,然后要求花费小于等于L的两个节点对的数量。花费的定义是,两个节点中有n条路,每条路是由几条边连成的,每条路中最大的边就是这条路的花费,然后两个节点间的花费就是这n条路中的花费的最小值。

这题的思路是先用kruskal合并路线,然后开个sum数组记录每个父节点所在集合的点的数量。

然后思想是如果两个节点已经在同一个集合中了,那么这条边就不取。why?因为kruskal是按照边权排序的,如果两个点在一个集合中,说明它们之间已经连通了,有路径了,并且那条路径的花费小于当前考虑的路径的花费,所以不取。

如果两个节点分别在A,B两个集合中,A中有sum[A]个点,B中有sum[B]个点,那么A中的点到B中的点都连通了,然后A中一点到B中一点有一条路径,这条路径的花费肯定是当前考察的edge(因为花费是路径中的最大值,而edge是按照权值排序的)。这样一下子就增加了sum[A]*sum[B]条路。

其实这题就离线输出这个坑点还有就是sum存储的要点,也是不难的。我仍需努力啊

#include<iostream>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<stack>
using namespace std;
#define MAX 10000+5
#define MAXN 50000+5
typedef long long LL;
const double pi=3.141592653589793;
const int INF=1e9;
const double inf=1e20;
const double eps=1e-10;
const int mod=1000000007;
int pre[MAX];
int sum[MAX];//记录父节点所在的集合中的点的数量
struct E{
	int u,v,de;
}edge[MAXN];
struct Q{
	int l,id,ans;//离线输出,所以先要按照l排序算出所有答案,然后按照id排序进行输出
}que[MAX];
bool cmp(struct E a,struct E b){
	return a.de<b.de;
}
bool cmp1(struct Q a,struct Q b){
	return a.l<b.l;
}
bool cmp2(struct Q a,struct Q b){
	return a.id<b.id;
}
int Find(int x){
	return pre[x]==x?x:pre[x]=Find(pre[x]);
}
int Union(int a,int b){
	int fx=Find(a);
	int fy=Find(b);
	int temp;
	if(fx==fy) return 0;//如果两个点在同一个集合中,他们之间有其他路径,其他路径中的最长边肯定小于edge[a][b];
	pre[fx]=fy;//fy为fx集合的父节点
	temp=sum[fx]*sum[fy];//两个集合相连,从一个fx集合的点到fy集合的点总共有sum[fx]*sum[fy]条路径,这些路径中最长值都是edge[a][b]
	sum[fy]+=sum[fx];//更新父节点集合中的点的数量
	return temp;
}
int main(){
	int n,m,q;
	while(~scanf("%d%d%d",&n,&m,&q)){
		for(int i=1;i<=n;i++){
			pre[i]=i;
			sum[i]=1;
		}
		for(int i=0;i<m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].de);
		for(int i=0;i<q;i++){
			scanf("%d",&que[i].l);
			que[i].id=i;
			que[i].ans=0;
		}
		sort(edge,edge+m,cmp);
		sort(que,que+q,cmp1);
		int cnt=0;
		for(int i=0;i<q;i++){
			while(edge[cnt].de<=que[i].l&&cnt<m){
				que[i].ans+=Union(edge[cnt].u,edge[cnt].v);
				cnt++;
			}
			if(i) que[i].ans+=que[i-1].ans;//l大的que肯定包括l小的que的解
		}
		sort(que,que+q,cmp2);
		for(int i=0;i<q;i++) printf("%d\n",que[i].ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值