并查集 - Restructuring Company - CodeForces - 566D
题意:
给 定 n 个 点 , q 次 询 问 。 给定n个点,q次询问。 给定n个点,q次询问。
首 行 输 入 n 和 q 。 首行输入n和q。 首行输入n和q。
接 着 q 行 输 入 包 括 三 个 整 数 , t , x , y 。 接着q行输入包括三个整数,t,x,y。 接着q行输入包括三个整数,t,x,y。
t 是 操 作 的 类 型 , x 和 y 是 点 的 编 号 。 t是操作的类型,x和y是点的编号。 t是操作的类型,x和y是点的编号。
询 问 分 为 三 种 : 询问分为三种: 询问分为三种:
① 、 t = 1 , 表 示 将 x 和 y 合 并 到 一 个 连 通 块 中 。 ①、t=1,表示将x和y合并到一个连通块中。 ①、t=1,表示将x和y合并到一个连通块中。
② 、 t = 2 , 表 示 将 区 间 [ x , y ] 的 节 点 全 部 合 并 到 同 一 个 连 通 块 中 。 ②、t=2,表示将区间[x,y]的节点全部合并到同一个连通块中。 ②、t=2,表示将区间[x,y]的节点全部合并到同一个连通块中。
③ 、 t = 3 , 表 示 查 询 x 和 y 是 否 再 同 一 个 连 通 块 中 。 若 是 , 输 出 ′ ′ Y E S ′ ′ ; 否 则 输 出 ′ ′ N O ′ ′ 。 ③、t=3,表示查询x和y是否再同一个连通块中。若是,输出''YES'';否则输出''NO''。 ③、t=3,表示查询x和y是否再同一个连通块中。若是,输出′′YES′′;否则输出′′NO′′。
Examples
Input:
8 6
3 2 5
1 2 5
3 2 5
2 4 7
2 1 2
3 1 7
Output:
NO
YES
YES
数据范围:
1 ≤ n ≤ 200 000 , 1 ≤ q ≤ 500 000 T i m e l i m i t : 2000 m s , M e m o r y l i m i t : 262144 k B 1 ≤ n ≤ 200 000, 1 ≤ q ≤ 500 000\\Time \ limit:2000 ms,Memory \ limit:262144 kB 1 ≤ n ≤ 200 000,1 ≤ q ≤ 500 000Time limit:2000ms,Memory limit:262144kB
分析:
利 用 并 查 集 , 很 容 易 快 速 进 行 ① 和 ③ 操 作 。 利用并查集,很容易快速进行①和③操作。 利用并查集,很容易快速进行①和③操作。
问 题 在 于 操 作 ② 。 问题在于操作②。 问题在于操作②。
由 于 n 和 q 的 范 围 较 大 , 操 作 ② 有 可 能 会 重 复 合 并 某 段 区 间 , 导 致 效 率 降 低 。 由于n和q的范围较大,操作②有可能会重复合并某段区间,导致效率降低。 由于n和q的范围较大,操作②有可能会重复合并某段区间,导致效率降低。
解 决 方 案 : 解决方案: 解决方案:
额 外 开 一 个 n e 数 组 , 来 记 录 每 个 节 点 合 并 后 , 下 一 次 需 要 合 并 的 节 点 编 号 。 额外开一个ne数组,来记录每个节点合并后,下一次需要合并的节点编号。 额外开一个ne数组,来记录每个节点合并后,下一次需要合并的节点编号。
举 例 : 合 并 区 间 [ l , r ] 。 举例:合并区间[l,r]。 举例:合并区间[l,r]。
首 次 合 并 , 我 们 是 逐 个 合 并 , 同 时 对 于 i ∈ [ l , r ] , 更 新 n e [ i ] = n e [ r ] 。 首次合并,我们是逐个合并,同时对于i∈[l,r],更新ne[i]=ne[r]。 首次合并,我们是逐个合并,同时对于i∈[l,r],更新ne[i]=ne[r]。
当 我 们 再 次 合 并 区 间 [ l ′ , r ′ ] 时 , 当我们再次合并区间[l',r']时, 当我们再次合并区间[l′,r′]时,
① . 若 l ′ < l ≤ r ′ ≤ r , 就 可 以 只 合 并 [ l ′ , l ] 这 一 部 分 节 点 , 因 为 [ l , r ′ ] 已 经 合 并 过 , n e [ l ] = n e [ r ] > r ′ , 此 时 循 环 会 终 止 。 ①.若l'<l≤r'≤r,就可以只合并[l',l]这一部分节点,因为[l,r']已经合并过,ne[l]=ne[r]>r',此时循环会终止。 ①.若l′<l≤r′≤r,就可以只合并[l′,l]这一部分节点,因为[l,r′]已经合并过,ne[l]=ne[r]>r′,此时循环会终止。
② . 若 l ≤ l ′ ≤ r ′ ≤ r , 因 为 n e [ l ′ ] = n e [ r ] > r ′ , 循 环 终 止 , 不 会 进 行 合 并 。 ②.若l≤l'≤r'≤r,因为ne[l']=ne[r]>r',循环终止,不会进行合并。 ②.若l≤l′≤r′≤r,因为ne[l′]=ne[r]>r′,循环终止,不会进行合并。
③ . 若 l < l ≤ r < r ′ , 与 ① 同 理 。 ③.若l<l≤r<r',与①同理。 ③.若l<l≤r<r′,与①同理。
这 样 就 能 保 证 同 一 个 连 通 块 的 点 只 会 被 合 并 一 次 。 这样就能保证同一个连通块的点只会被合并一次。 这样就能保证同一个连通块的点只会被合并一次。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
const int N=2e5+10;
int n,m;
int p[N];
int ne[N];
int Find(int x)
{
if(p[x]!=x) return p[x]=Find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) p[i]=i,ne[i]=i+1;
int t,a,b;
while(m--)
{
scanf("%d%d%d",&t,&a,&b);
int pa=Find(a),pb=Find(b);
if(t==1) p[pa]=pb;
else if(t==2)
{
int ni;
for(int i=a+1;i<=b;i=ni)
{
p[Find(i)]=Find(i-1);
ni=ne[i];
ne[i]=ne[b];
}
}
else
{
if(pa==pb) puts("YES");
else puts("NO");
}
}
return 0;
}