题目简介
给出两种指令:M和C。
M i j:将
i
i
i 插入到
j
j
j 的末尾,按照顺序将整个序列插入。
C i j:查询
i
i
i 和
j
j
j 节点之间的元素个数。
洛谷P1196跳转链接
解题思路
首先注意到,如果有节点p,而它的子节点l1,l2,l3的father都是p。
那么我们只需要修改p,l1,l2,l3的值就可以顺便修改了(类似LazyTag延迟更新),当查询到l1,l2,l3头上的时候,再去更新。
现在设计三个数组:Index、Fa、Nums数组。
第一个Index数组用于保存此节点全局的编号。第二个保存这个节点的爸爸。而第三个保存的是此节点往后的所有元素个数。
比如序列【(1)、(2)、(3)】节点的Index是1,2,3
而序列【(4)、(5)】节点Index是1,2
那么现在想要【(4)、(5)、(1)、(2)、(3)】的话,正确的节点Index应该是,1,2,3,4,5但是现在却是1,2,1,2,3。
好在,我们的(2)、(3)指向(1)因为(1)是他们爸爸。而(5)指向(4)。并查集合并的时候使用(1)指向(4)然后因为知道了【(1),(2),(3)】元素的个数,所以可以修改Nums[(4)]=2(之前是两个元素)+3(又加入3个元素);
然后可以修改全局索引Index[(1)]=3,因为我们知道之前的Nums[(4)]有两个。所以说,现在是1,2,3,2,3。
那么后面两个2,3怎么更正呢?通过查询自己父节点Index[(1)]=3,可以知道如果它之前没有链接其他序列,它应该是Index[(1)]=1,所以我们知道偏移量是2。因为头部多了两个元素,所以,后面的肯定也是两个元素。在Query的过程中,就可以轻松更正Index[(2)]=2+2=4查询(3)的时候也可以轻松更正Index[(3)]=3+2=5.
所以最后:【(4)、(5)、(1)、(2)、(3)】的整个全局的Index是:1,2,3,4,5。啦!
现在,你已经知道了,整个序列元素是5个·,也知道了Index的编号,那么Nums还不好更新吗?So Easy了~找找关系就好啦!
(っ °Д °;)っ
AC代码
// VsCode C++模板 | (●'◡'●)
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
typedef long long LL;
#define MaxN 30000 + 5
int T, Index[MaxN], Fa[MaxN], Nums[MaxN];
int QueryRoot(int node) {
if (Fa[node] == node) { return node; }
auto root = QueryRoot(Fa[node]);
int& node_root = Fa[node]; // now root
int _move = Index[node_root] - 1;
Index[node] += _move;
Nums[node] = Nums[root] - Index[node] + 1;
node_root = root;
return root;
}
void Mergen(int i, int j) {
int rootI = QueryRoot(i);
int rootJ = QueryRoot(j);
Index[rootI] += Nums[rootJ];
Nums[rootJ] += Nums[rootI];
Fa[rootI] = rootJ;
}
int main() {
for (int i = 1;i <= MaxN - 5;i++) {
Index[i] = 1;
Nums[i] = 1;
Fa[i] = i;
}
scanf("%d", &T);
while (T--) {
char opt; int i, j;
cin >> opt >> i >> j;
if (opt == 'M') { //Merg
Mergen(i, j);
} else { //Query
int rootI = QueryRoot(i), rootJ = QueryRoot(j);
if (rootI != rootJ) printf("-1\n"); else {
printf("%d\n", abs(Index[i] - Index[j]) - 1);
}
}
}
return 0;
}