Power OJ 2825:拔河大赛(并查集)

传送门

这道题要检查两个人之间是不是互相认识,很明显这里需要用到并查集的知识,只是在这里我们要维护一下队伍的人数和队伍的最大编号
思路:我们在这里要多开两个数组,一个是用来维护队伍人数的cnt,一个是维护对应的队伍人数下该队里面编号最大的ans,我们在并查集去检查两个人之间是否认识的时候,如果不认识,继续判断一下他们的根节点谁大,在这里我们把根节点小的改成根节点大的,然后把以根节点为下标的cnt他们加起来,也就是cnt[max]=cnt[max]+cnt[min],为什么要这么做呢,因为我们后面查询每一个点的时候就可以直接找到他们的根节点,直接只跟新与根节点有关的信息,然后其他的信息就可以不用更改了(也就是找到根节点的root,对应的cnt[root]就等于他的队伍人数),然后我们再去更改ans数组的时候,ans[i]即表示人数为i的队伍的队长编号,然后我们直接循环一遍找每一个人的根节点,然后根据根节点找到cnt[root]即队伍人数,然后修改对应的ans[cnt[i]]=root,t因为在这里root一定是队伍里面最大的(前面已经判断过了),然后打一遍表,意思就是更新0-n个人数的队伍的最优解
下面给出代码

#include<bits/stdc++.h>
#define lson l, mid, root << 1
#define rson mid + 1, r, root << 1 | 1
#define father  l , r , root
#define lowbit(x) ( x & ( - x ) )
#define mod 1000000007
using namespace std;
typedef long long ll;
const int maxn = 5e5+10;
const int inf = 0x3f3f3f3f;
int fa[maxn];
int cnt[maxn];
int per[maxn];
int ans[maxn];
struct node{
    
    
	int id,num;
}a[maxn];
int Find(int x){
    
    
    if(fa[x]==x)return x;
    return fa[x]=Find(fa[x]);
}
int main( ){
    
    
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    memset(ans,0,sizeof(int));
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=n;i++)cnt[i]=1;
	for(int i=1;i<=m;i++){
    
    
		int c,d;
		scanf("%d%d",&c,&d);
		int f1=Find(c);
		int f2=Find(d);
		if(f1!=f2){
    
    
			if(f1>f2){
    
    
				fa[f2]=f1;
				cnt[f2]=cnt[f1]+cnt[f2];
			}else{
    
    
				fa[f1]=f2;
				cnt[f2]=cnt[f1]+cnt[f2];//队伍的人数 
			}
		}
	}
		for(int i=1;i<=n;i++){
    
    
			int f=Find(i);
			ans[cnt[f]]=ans[cnt[f]];//ans[i]存的是  由i个人组成的队伍,最大编号 
		}
		a[0].id=-1,a[0].num=-1;
		for(int i=1;i<=n;i++){
    
    
			if(ans[i]==0){
    
    
				a[i]=a[i-1];
			}else{
    
    
				a[i].id=i;
				a[i].num=ans[i]; 
			}
		}
		for(int i=0;i<q;i++){
    
    
			int w;
			scanf("%d",&w);
			if(a[w].id==-1){
    
    
				printf("%d\n",-1);
			}else{
    
    
				printf("%d %d\n",a[w].num,a[w].id);
			}
		}	 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43715171/article/details/98230604
今日推荐