最重要的思想是,线段树里存的是值为L到R的个数,即在L-R这段区间内,值为L,L+1,L+2,L+3…R的个数。
#include<cstdio>
#include<cstring>
const int maxn = 2e5+10;
#define mid (l+r)>>1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int sum[maxn<<2];//记录L到R的范围内,值为L,L+1,L+2...R-1,R的个数
int a[maxn],p[maxn];
int n;
void build(int l,int r,int rt)
{
if(l==1)
sum[rt]=n;//若有10个点,则值为1的点有10个
else
sum[rt]=0;//其他值都为0
if(l==r)
return ;
int m=mid;
build(lson);
build(rson);
}
void update(int l,int r,int rt,int val,bool flag)
{
if(!flag)
sum[rt]--;
else
sum[rt]++;
int m=mid;
if(l==r)
return ;
if(val<=m)
update(lson,val,flag);
else
update(rson,val,flag);
}
int find(int x)
{
return x==p[x] ? x : p[x]=find(p[x]);
}
void query(int l,int r,int rt,int k)
{
if(l==r)
{
printf("%d\n",l);
return ;
}
int m=mid;
if(k<=sum[rt<<1|1])
query(rson,k);//右子树有大于等于k个数,第k个数肯定在右边
else
query(lson,k-sum[rt<<1|1]);
}
int main()
{
int i,m,q,x,y,k;
scanf("%d%d",&n,&m);
for(i=1; i<=n; i++)
{
a[i]=1; //根据题意,初始化每个点的值
p[i]=i; //存每个点的父节点
}
build(1,n,1); //建树
for(i=1; i<=m; i++)
{
scanf("%d",&q);
if(q==0)
{
scanf("%d%d",&x,&y);
x=find(x); //查找x的父节点
y=find(y); //查找y的父节点
if(x==y) //如果x和y已经属于一个组,就不用再合并
continue;
update(1,n,1,a[x],false); //将记录值为a[x]有几个数的点减1
update(1,n,1,a[y],false);
update(1,n,1,a[x]+a[y],true); //将值为a[x]+a[y]的点加1
p[y]=x; //并查集,将x和y归为一组
a[x]+=a[y]; //用a[x]记录这个组的值为多少
}
else
{
scanf("%d",&k);
query(1,n,1,k);
}
}
return 0;
}