O - 土豆的集合

O - 土豆的集合

题目大意

查询某两个点是否联通,或者将某两个点联通,支持查询历史版本。

题解

如果不需要查询历史版本,那么显然就是并查集。

要支持历史版本,那就可持久化一下。

但是并查集不能路径压缩,为了保证时间复杂度,需要按秩合并

  • 注意:要及时更新root数组,即便是需要连接的两个点已经联通。

时间复杂度

按秩合并后的并查集,每一次查询是 O ( log ⁡ n ) O(\log n) O(logn),可持久化的时间复杂度是 O ( log ⁡ n ) O(\log n ) O(logn)

因此,总的时间复杂度为 O ( m × log ⁡ 2 n ) O(m\times \log^2 n) O(m×log2n)

Tag

可持久化

并查集

code

//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define G getchar
#define ls(x) tr[x].l
#define rs(x) tr[x].r
#define s(x) tr[x].s
#define f(x) tr[x].fa
using namespace std;

int read()
{
    char ch;
    for(ch = G();(ch < '0' || ch > '9') && ch != '-';ch = G());
    int n = 0 , w;
    if (ch == '-')
    {
        w = -1;
        ch = G();
    } else w = 1;
    for(;'0' <= ch && ch <= '9';ch = G())n = (n<<1)+(n<<3)+ch-48;
    return n * w;
}

const int N = 300005;
int n , m , t , l , r , opv , tot , op , x , y , rt , ops , opf , ls;
int root[N * 100];

struct node
{
    int l , r , s , fa;
}tr[N*100];

void ins (int x , int xx , int l , int r)
{
    if (l == r)
    {
        f(xx) = opf;
        s(xx) = ops;
        return;
    }
    int m = (l + r) >> 1;
    if (opv <= m)
    {
        rs(xx) = rs(x);
        ls(xx) = ++ tot;
        ins(ls(x) , ls(xx) , l , m);
    }
    else
    {
        rs(xx) = ++ tot;
        ls(xx) = ls(x);
        ins(rs(x) , rs(xx) , m + 1 , r);
    }
}

int find (int x , int l , int r)
{
    if (l == r) return x;
    int m = (l + r) >> 1;
    if (opv <= m)return find(ls(x) , l , m);
        else return find(rs(x) , m + 1 , r);
}

int pos (int x)
{
    opv = x;
    return find(rt , 1 , n);
}

int get (int x)
{
    int t = pos(x);
    if (f(t) == x) return t;
        else return get(f(t));
}

int main()
{
    freopen("o.in","r",stdin);
    //freopen("o.out","w",stdout);
    n = read(); m = read();
    ls = tot = 0;
    ops = 1;
    for (int i = 1; i <= n; ++i)
    {
        opf = opv = i;
        root[0] = ++ tot;
        ins(ls , tot , 1 , n);
        ls = root[0];
    }
    for (int i = 1; i <= m; ++i)
    {
        op = read();
        if (op == 2)
        {
            root[i] = root[read()];
            continue;
        }
        l = read();
        r = read();
        if (op == 3)
        {
            rt = root[i] = root[i - 1];
            x = get(l);
            y = get(r);
            if (f(x) ^ f(y))puts("0");
                else puts("1");
        }
        else
        {
            rt = root[i - 1];
            x = get(l);
            y = get(r);
            if (f(x) ^ f(y))
            {
                if (s(x) < s(y)) swap(x , y);
                ls = ++ tot;
                opv = f(y);
                opf = f(x);
                ops = s(y);
                ins(root[i - 1] , ls , 1 , n);

                root[i] = ++ tot;
                opv = f(x);
                opf = f(x);
                ops = max(s(y) + 1 , s(x));
                ins(ls , root[i] , 1 , n);
            }else root[i] = rt;
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值