并查集 - Restructuring Company - CodeForces - 566D

并查集 - Restructuring Company - CodeForces - 566D

题意:

给 定 n 个 点 , q 次 询 问 。 给定n个点,q次询问。 nq

首 行 输 入 n 和 q 。 首行输入n和q。 nq

接 着 q 行 输 入 包 括 三 个 整 数 , t , x , y 。 接着q行输入包括三个整数,t,x,y。 qtxy

t 是 操 作 的 类 型 , x 和 y 是 点 的 编 号 。 t是操作的类型,x和y是点的编号。 txy

询 问 分 为 三 种 : 询问分为三种:

① 、 t = 1 , 表 示 将 x 和 y 合 并 到 一 个 连 通 块 中 。 ①、t=1,表示将x和y合并到一个连通块中。 t=1xy

② 、 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=3xyYESNO

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 1n200000,1q500000Time limit:2000msMemory limit:262144kB


分析:

利 用 并 查 集 , 很 容 易 快 速 进 行 ① 和 ③ 操 作 。 利用并查集,很容易快速进行①和③操作。

问 题 在 于 操 作 ② 。 问题在于操作②。

由 于 n 和 q 的 范 围 较 大 , 操 作 ② 有 可 能 会 重 复 合 并 某 段 区 间 , 导 致 效 率 降 低 。 由于n和q的范围较大,操作②有可能会重复合并某段区间,导致效率降低。 nq

解 决 方 案 : 解决方案:

额 外 开 一 个 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<lrr[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',循环终止,不会进行合并。 .llrrne[l]=ne[r]>r

③ . 若 l < l ≤ r < r ′ , 与 ① 同 理 。 ③.若l<l≤r<r',与①同理。 .l<lr<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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值