HRBUST - 2371(权值线段树+优化并查集)

HRBUST - 2371
题意:在现实中认了无数师傅却毫无长进的GT在梦中成为了某武侠世界的神。在这个世界中初始有n个人,他们各成一派。作为世界神GT总共会进行m次操作,每次操作有如下两种情况
1 x y 表示x所在的帮派吞并了y所在的帮派,若x与y本来就处于同一个帮派则该操作无效。
2 k 表示GT想要知道当前第k大的帮派有多少人,若当前帮派数量少于k个则输出-1。
题解:每次如果两个集合合并就分别让两个集合所属的权值线段树区间减1,而合并的大集合所对应的区间加1,不过并查集要用优化版的(也就是将并查集压缩一下,将树压矮一点)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=100005;
int sum[maxn<<2];
int p[maxn],num[maxn];
int n,m,pren;
void build(int l,int r,int rt)
{
	if(l==r){
		if(l==1)
		sum[rt]=n;
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int finds(int x)
{
	//return x==p[x]?x:finds(p[x]);
    if(x==p[x]){
    	return x;
	}
	else{
		int px=finds(p[x]);
		p[x]=px;
		return px;
	}
}
void update1(int l,int r,int rt,int x)
{
	if(l==r){
		sum[rt]-=1;
		return;
	}
	int mid=(l+r)>>1;
    if(x<=mid){
    	update1(l,mid,rt<<1,x);
	}
	else{
		update1(mid+1,r,rt<<1|1,x);
	}
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update2(int l,int r,int rt,int x)
{
	if(l==r){
		sum[rt]+=1;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid){
		update2(l,mid,rt<<1,x);
	}
	else{
		update2(mid+1,r,rt<<1|1,x);
	}
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query(int l,int r,int rt,int k)
{
    if(l==r){
    	return l;
	}
	int mid=(l+r)>>1;
	if(sum[rt<<1]>=k){
		return query(l,mid,rt<<1,k);
	}
	else{
		return query(mid+1,r,rt<<1|1,k-sum[rt<<1]);
	}
}
inline int read()
{
    register int x=0,t=1;
    register char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-'){t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    return x*t;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
    	//memset(sum,0,sizeof(sum));
    	for(int i=1;i<=4*n;i++){
    		sum[i]=0;
		}
       scanf("%d%d",&n,&m);
       pren=n;
       for(int i=1;i<=n;i++){
       	p[i]=i;num[i]=1;
	   }
       build(1,n,1);
       /*for(int i=1;i<=4*n;i++){
       	printf("%d ",sum[i]);
	   }
	   printf("\n");*/
       int op,x,y,dx,dy;
       while(m--){
       	scanf("%d",&op);
       	if(op==1){
       		x=read();
       		y=read();
       		//scanf("%d%d",&x,&y);
       		dx=finds(x);
       		dy=finds(y);
       		//printf("koo\n");
       	//	printf("%d %d\n",num[dx],num[dy]);
       		if(dx!=dy){
       		//	printf("dx:%d dy:%d\n",dx,dy);
       		//	printf("num[dx]:%d num[dy]:%d\n",num[dx],num[dy]);
       			update1(1,n,1,num[dx]);
       			update1(1,n,1,num[dy]);
       			update2(1,n,1,num[dx]+num[dy]);
       			p[dx]=dy;
       			num[dy]+=num[dx];
       			//printf("num[dx]:%d num[dy]:%d\n",num[dx],num[dy]);
       			pren--;
			   }
		   }
		   else{
		   	scanf("%d",&x);
		   	if(x>pren){
		   		printf("-1\n");
			   }
			   else{
		   	      printf("%d\n",query(1,n,1,pren+1-x));
		       }
		   }
		   /*for(int i=1;i<=4*n;i++){
		   	printf("%d ",sum[i]);
		   }
		   printf("\n"); 
		   printf("pren:%d\n",pren);*/
	   }
	}
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值