题意:给n个人排队,开始站成一排。
两种操作,T操作:让编号Y的同学所在队伍接到编号X同学后,顺序不变,若在一个队伍,不移动
Q操作:询问编号X同学身后有多少同学。
样例输入:2 2(n,m,n个同学,m次操作,1<=n,m<=35000)
T 1 2
Q 1
输出: 1
这题用并查集写,num数组保存i所在的队列一共多少人,ans[i]数组记录i后有多少人。(表示开始想这的时候还是想简单了)
因为并查集都是在祖节点(不加分支)进行查和并的工作,如果按正常思维,我把编号2放到1后面,3放到2后,1是2,3的祖先,那么我下次就没办法确定我的队尾是2还是3了,所以,我们把每次队尾当祖先,即2是1的祖先,3是2的祖先,这样就确定了3是队尾,下次再插入队尾就可以直接合并,然后就是查询编号X后面的个数,在合并的时候,我每次只把队尾(祖先)的ans数组改变,这样就确定了队尾后面的人数,然后查询的时候用递归回溯调整子节点(前一个人)ans值。
#include<bits/stdc++.h>
using namespace std;
const int maxn=350010;
int pre[maxn],num[maxn],ans[maxn];
int n,m;
void init(){
scanf("%d %d",&n,&m);//初始化
for(int i=1;i<=n;i++){
pre[i]=i;
num[i]=1;
ans[i]=0;
}
}
int find(int x){
if(x==pre[x]) return x;
int temp=pre[x];//记录父亲节点
pre[x] = find(pre[x]);
ans[x] += ans[temp];//回溯时候是从队尾开始往下的
return pre[x];
}
void un(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx!=fy){
pre[fx]=fy; //队尾为祖先
ans[fx] = num[fy]; //把队尾的ans值变成加入队列的总人数
num[fy] += num[fx];//把当前队尾所确定的总人+=上次队尾的总人数
}
}
int main(){
init();
char c;
int a,b;
for(int i=1;i<=m;i++){
scanf(" %c",&c);
if(c=='T'){
scanf(" %d %d",&a,&b);
un(a,b);
}else{
scanf(" %d",&a);
find(a);
printf("%d\n",ans[a]);
}
}
return 0;
}