传送门
这道题要检查两个人之间是不是互相认识,很明显这里需要用到并查集的知识,只是在这里我们要维护一下队伍的人数和队伍的最大编号
思路:我们在这里要多开两个数组,一个是用来维护队伍人数的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;
}