有n个人在公司里面工作。员工从1到n编号。每一个人属于一个部门。刚开始每一个人在自己的部门负责自己的项目,这样的话公司里面就有n个部门。
然而,公司内部出现了危机,需要合并一些部门,以提高工作效率。team(person) 表示person这个人所在的部门。有以下两种合并操作:
1. 合并 team(x) 和 team(y)。 x和 y (1≤x,y≤n)是员工编号。如果team(x) 和 team(y)是同一个部门,那么就不操作。
2. 合并team(x),team(x+1),...,team(y),x 和 y (1≤x≤y≤n)是员工编号。
有一些查询操作,查询员工x 和 y (1≤x,y≤n)是否属于同一部门。
Input
单组测试数据。
第一行有两个整数n 和 q (1≤n≤200000, 1≤q≤500000)表示员工的数目和操作数目。
接下来q行,每行的格式是type x y。type∈{1,2,3}。如果type=1 或者 type=2,那么表示第一种或者第二种合并操作。如果type=3,表示查询员工x和y是否属于同一部门。
Output
对于第三种查询,如果属于同一部门输出YES,否则输出NO。
Input示例
样例输入1
8 6
3 2 5
1 2 5
3 2 5
2 4 7
2 1 2
3 1 7
Output示例
样例输出1
NO
YES
YES
思路:
并查集,par数组用来记录各节点的父子关系,prev数组是用来记录每个节点左边最近的临界点,主要是为了避免逐个查找范围内的每一个节点。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
const int MAXN = 200005;
int par[MAXN];
int prev[MAXN];
int read()
{
char temp;
int x = 0;
while (isdigit(temp = getchar()))
{
x = x * 10 + temp - '0';
}
return x;
}
int find(int *p, int x)
{
if (x == p[x])
{
return x;
}
else
{
p[x] = find(p, p[x]);
}
return p[x];
}
void merge(int *p, int x, int y)
{
p[find(p, y)] = find(p, x);
}
void mergeXY(int x, int y)
{
y = find(prev, y);
while (y > x)
{
int t = find(prev, y - 1);
merge(prev, t, y);
merge(par, t, y);
y = t;
}
}
int main()
{
int n, q;
n = read();
q = read();
for (int i = 1; i <= n; ++i)
{
par[i] = i;
prev[i] = i;
}
int t, x, y;
while (q--)
{
t = read();
x = read();
y = read();
if (t == 1)
{
merge(par, x, y);
}
else if (t == 2)
{
mergeXY(x, y);
}
else
{
puts(find(par, x) == find(par, y)? "YES" : "NO");
}
}
return 0;
}