BZOJ 3673 && BZOJ 3674 [可持久化线段树]

本文介绍了一种数据结构——可持久化线段树的实现方法,并通过一个具体的集合合并问题实例展示了其应用过程。文章详细解释了如何通过启发式合并等技巧优化集合操作,同时提供了完整的代码实现。

n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
0<n,m21040<n,m2105

其实就是要打一个可持久化数组。
其实就是要打一个可持久化线段树QAQ
其实一直很想学来着QAQ
以前是打过动态开点线段树。
现在想想其实有些相似诶
合并集合的时候好像可以启发式合并诶
是按集合的轶合并的QAQ
大概是这样的啦

void Modify(int &o, int x, int l, int r, int pos, int k) {
    o = ++Tcnt;
    if (l == r) {
        fa[o] = k; Rank[o] = Rank[x];
        return;
    }
    ls[o] = ls[x]; rs[o] = rs[x];
    int mid = (l + r) >> 1;
    if (pos <= mid) Modify(ls[o], ls[x], l, mid, pos, k);
    else Modify(rs[o], rs[x], mid + 1, r, pos, k);
}
if (Rank[p1] > Rank[p2]) swap(p1, p2);
Modify(rt[i], rt[i - 1], 1, n, fa[p1], fa[p2]);
if (Rank[p1] == Rank[p2]) Add(rt[i], 1, n, fa[p2]);

终于算打过可持久化线段树了QAQ

#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;

const int N = 10000100;

inline char get(void) {
    static char buf[1000000], *p1 = buf, *p2 = buf;
    if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin);
        if (p1 == p2) return EOF;
    }
    return *p1++;
}
inline void read(int &x) {
    x = 0; char c = get(); int sign = 1;
    for (; c < '0' || c > '9'; c = get()) if(c == '-') sign = 0;
    for (; c >= '0' && c <= '9'; x = (x << 1) + (x << 3) + c - '0', c = get());
    x = sign ? x : -x;
}

int rt[200010], ls[N], rs[N], fa[N], Rank[N];
int n, m, x, y, opr, Tcnt, p1, p2, last;

void Build(int &o, int l, int r) {
    if (!o) o = ++Tcnt;
    if (l == r) {
        fa[o] = l; return;
    }
    int mid = (l + r) >> 1;
    Build(ls[o], l, mid);
    Build(rs[o], mid + 1, r);
}
int Query(int o, int l, int r, int pos) {
    if (l == r) return o;
    int mid = (l + r) >> 1;
    if (pos <= mid) return Query(ls[o], l, mid, pos);
    else return Query(rs[o], mid + 1, r, pos);
}
void Modify(int &o, int x, int l, int r, int pos, int k) {
    o = ++Tcnt;
    if (l == r) {
        fa[o] = k; Rank[o] = Rank[x];
        return;
    }
    ls[o] = ls[x]; rs[o] = rs[x];
    int mid = (l + r) >> 1;
    if (pos <= mid) Modify(ls[o], ls[x], l, mid, pos, k);
    else Modify(rs[o], rs[x], mid + 1, r, pos, k);
}
void Add(int o, int l, int r, int pos) {
    if (l == r) {
        Rank[o]++; return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) Add(ls[o], l, mid, pos);
    else Add(rs[o], mid + 1, r, pos);
}
int find(int rt, int x) {
    int Pos = Query(rt, 1, n, x);
    if (x == fa[Pos]) return Pos;
    return find(rt, fa[Pos]);
}

int main(void) {
    freopen("1.in", "r", stdin);
    read(n); read(m);
    Build(rt[0], 1, n);
    for (int i = 1; i <= m; i++) {
        read(opr);
        if (opr == 1) {
            read(x); read(y); rt[i] = rt[i - 1];
            x ^= last; y ^= last;
            p1 = find(rt[i], x); p2 = find(rt[i], y);
            if (fa[p1] == fa[p2]) continue;
            if (Rank[p1] > Rank[p2]) swap(p1, p2);
            Modify(rt[i], rt[i - 1], 1, n, fa[p1], fa[p2]);
            if (Rank[p1] == Rank[p2]) Add(rt[i], 1, n, fa[p2]);
        } else if (opr == 2) {
            read(x); x ^= last; rt[i] = rt[x];
        } else {
            read(x); read(y); rt[i] = rt[i - 1];
            x ^= last; y ^= last;
            p1 = find(rt[i], x); p2 = find(rt[i], y);
            if (fa[p1] == fa[p2]) {
                puts("1"); last = 1;
            } else {
                puts("0"); last = 0;
            }
        }
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值