题意
有两个名为xxx的神奇数据结构,xxx AAA 有最多 300003000030000 个点,开始每行都只有一个点。
有时会给出合并指令,格式为 MMM iii jjj,每次都将处在第iii个点所在行整体移到第 jjj 个点所在行的最后。
有时会给出查询指令,格式为 CCC iii jjj,询问。
分析
对于这道题,首先不要被它的NOI身份(就是个古早的t1)和较长的描述(实际也不长)吓到。
仔细观察,因为要用并查集,所以可以把每一行的第一个点就可以看成根节点,这就具备了并查集的基本形状。
可以把问题分成几个小问题:
-
记录每个点在哪一行:
因为这是并查集的题,所以可以用并查集实现。 -
计算同一行内两个点的距离:执行 findfindfind 操作时,因为要对 preprepre 数组进行路径压缩(不然容易炸空间&时间),所以较难直接用 preprepre 求解,再开一个 disdisdis 数组,直接记录每一个点到它的根节点的距离,需要时直接做差求绝对值就好了。
-
实现题目中的合并操作:一般并查集的合并操作是直接合并每棵树的根节点(改变 preprepre 数组),不会改变其他如 disdisdis 的值,所以可以用 numnumnum 数组,记录每一排的节点数,每次合并时直接在 disidis_idisi 上加 numjnum_jnumj,就可以对同一排内的点进行顺序区分。
可以看到,每次 disidis_idisi 变化时,都会加上 numjnum_jnumj,但 prefind(i)pre_{find(i)}prefind(i) 可以直接连到 prefind(j)pre_{find(j)}prefind(j) 上,这样就实现了合并。
那么查询其实就是求从 iii 到 jjj 的节点数,因为有 disdisdis 记录两点到第一个点的距离,就可以直接转化为求 abs(disi−disj)abs(dis_i-dis_j)abs(disi−disj)。
end.
Code
#include<bits/stdc++.h>
using namespace std;
char f;
int t,a,b,x,y,pre[30001],dis[30001],num[30001];
int find(int x){//带权并查集的路径压缩find
if (pre[x]==x)return x;
int sum=find(pre[x]);
dis[x]+=dis[pre[x]];
return pre[x]=sum;
}
int main(){
scanf("%d",&t);
for (int i=1;i<=30000;++i){
pre[i]=i,num[i]=1;
}
for (int i=1;i<=t;++i){
cin>>f>>a>>b;
x=find(a),y=find(b);
if (f=='M'){
dis[x]=num[y];//x点到了末尾
num[y]+=num[x];//b排要加上a排原数
num[x]=0;//a排已经并到b排,所以没有战舰
pre[x]=y;//真正的合并
}
else{
if (x!=y){
puts("-1");
}
else{
printf("%d\n",abs(dis[a]-dis[b])-1);
}
}
}
}