(题面来自洛谷)

题目描述

n个集合 m个操作

操作:

1 a b 合并a,b所在集合

2 k 回到第k次操作之后的状态(查询算作操作)

3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

\(n \le 10^5, m \le 2\times 10^5\)

考虑不带路径压缩、使用启发式合并的并查集,每一次合并实际上只是改变了两个点的信息。

1. v的父亲置为u

2. \(size(u) += size(v)\)

那么将数组fa、size改为可持久化数组维护即可。

复杂度分析:根据启发式合并性质,每次Find操作会执行\(logn\)次循环,循环中为可持久化数组查询,故Find操作的单次复杂度为\(O(log^2n)\)。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int maxn(200010);
int n, m;
struct Seg_tree {
#define mid ((l + r) >> 1)
#define lc(nd) seg[nd].lc
#define rc(nd) seg[nd].rc struct node {
int dat, lc, rc;
/* node(int a = 0, int b = 0, int c = 0):
dat(a), lc(b), rc(c) {}*/
// node(): dat(0), lc(0), rc(0) {}
} seg[maxn * 40];
int root[maxn], tot;
void modify(int& nd, int pre, int l, int r, int pos, int x) {
nd = ++tot;
seg[nd] = seg[pre];
if (l == r) {
seg[nd] = (node) {x, 0, 0};
return;
}
if (pos <= mid) modify(lc(nd), lc(pre), l, mid, pos, x);
else modify(rc(nd), rc(pre), mid+1, r, pos, x);
}
void build(int &nd, int l, int r, int val) {
nd = ++tot;
if (l == r) {
seg[nd] = (node) {val, 0, 0};
return;
}
build(lc(nd), l, mid, val);
build(rc(nd), mid+1, r, val);
return;
}
int query(int nd, int l, int r, int pos) {
if (!nd) return 0;
if (l == r) return seg[nd].dat;
if (pos <= mid) return query(lc(nd), l, mid, pos);
return query(rc(nd), mid+1, r, pos);
}
} Dsu, Siz;
int Find(int x, int ver) {
int tmp;
while (tmp = Dsu.query(Dsu.root[ver], 1, n, x)) x = tmp;
return x;
}
inline void merge(int u, int v, int ver) {
u = Find(u, ver), v = Find(v, ver);
if (u == v) return;
int a, b;
if ((a = Siz.query(Siz.root[ver], 1, n, u)) < (b = Siz.query(Siz.root[ver], 1, n, v))) swap(u, v);
Dsu.modify(Dsu.root[ver], Dsu.root[ver-1], 1, n, v, u);
Siz.modify(Siz.root[ver], Siz.root[ver-1], 1, n, u, a + b);
return;
}
int main() {
// freopen("test.in", "r", stdin);
// freopen("test.ans", "w", stdout);
scanf("%d %d", &n, &m);
Siz.build(Siz.root[0], 1, n, 1);
int op, u, v;
for (int i = 1; i <= m; ++i) {
scanf("%d %d", &op, &u);
if (op == 1) {
Siz.root[i] = Siz.root[i-1];
Dsu.root[i] = Dsu.root[i-1];
scanf("%d", &v);
merge(u, v, i);
} else if (op == 2) {
Siz.root[i] = Siz.root[u];
Dsu.root[i] = Dsu.root[u];
} else {
Siz.root[i] = Siz.root[i-1];
Dsu.root[i] = Dsu.root[i-1];
scanf("%d", &v);
putchar(Find(u, i) == Find(v, i) ? '1' : '0');
putchar('\n');
}
}
return 0;
}

【模板】【P3402】可持久化并查集的更多相关文章

  1. bzoj3673 & bzoj3674 & 洛谷P3402 可持久化并查集

    题目:bzoj3673:https://www.lydsy.com/JudgeOnline/problem.php?id=3673 bzoj3674:https://www.lydsy.com/Jud ...

  2. 「luogu3402」【模板】可持久化并查集

    「luogu3402」[模板]可持久化并查集 传送门 我们可以用一个可持久化数组来存每个节点的父亲. 单点信息更新和查询就用主席树多花 一个 \(\log\) 的代价来搞. 然后考虑如何合并两个点. ...

  3. 洛谷P3402 【模板】可持久化并查集 [主席树,并查集]

    题目传送门 可持久化并查集 n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 ...

  4. 【洛谷 P3402】 【模板】可持久化并查集

    题目链接 可持久化并查集,就是用可持久化线段树维护每个版本每个节点的父亲,这样显然是不能路径压缩的,否则我们需要恢复太多状态. 但是这并不影响我们启发式合并,于是,每次把深度小的连通块向深度大的上并就 ...

  5. 洛谷P3402 可持久化并查集

    n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 说是可持久化并查集,实际上是 ...

  6. 洛谷P3402 【模板】可持久化并查集(可持久化线段树,线段树)

    orz TPLY 巨佬,题解讲的挺好的. 这里重点梳理一下思路,做一个小小的补充吧. 写可持久化线段树,叶子节点维护每个位置的fa,利用每次只更新一个节点的特性,每次插入\(logN\)个节点,这一部 ...

  7. P3402 可持久化并查集

    P3402 通过主席树维护不同版本的并查集,注意要采用按秩合并的方式,路径压缩可能会爆. 1 #include <bits/stdc++.h> 2 using namespace std; ...

  8. P3402 【模板】可持久化并查集

    传送门 //minamoto #include<bits/stdc++.h> using namespace std; #define getc() (p1==p2&&(p ...

  9. 洛谷P3402 【模板】可持久化并查集

    一定注意每一次都要是 $root[cur]=root[cur-1]$,不然进行合并时如果 $a,b$ 在同一集合中就会使 $root[cur]=0$. Code: #include <cstdi ...

  10. Luogu3402【模板】可持久化并查集 (主席树)

    用\(depth\)按秩合并,不能直接启发,数组开40倍左右 #include <iostream> #include <cstdio> #include <cstrin ...

随机推荐

  1. Mybatis---02Mybatis执行过程分析

    1.在动态代理中,执行MapperMethod类里面的execute方法,这个方法里面最终是调用DefaultSqlSession类中的相关操作方法.接着之前的文章继续,在DefaultSqlSess ...

  2. 4G工业路由器的性能介绍和应用需求

    4G工业路由器可以实现数据的远程传输和设备控制功能,主要应用的场景包括智能电网.智能交通.智能家居.才智金融.工业自动化.公共安全.环境保护.数字化医疗等领域,特别是大数据或是视频传输等.那么4G工业 ...

  3. websocket报400错误

    解决方案看了下讨论区说的方案,问题出现在nginx的配置文件,需要修改nginx.conf文件.在linux终端中敲入vim /etc/nginx/nginx.conf,找到location这个位置, ...

  4. Java并发队列与容器

    [前言:无论是大数据从业人员还是Java从业人员,掌握Java高并发和多线程是必备技能之一.本文主要阐述Java并发包下的阻塞队列和并发容器,其实研读过大数据相关技术如Spark.Storm等源码的, ...

  5. Masking Personal Information

    Masking Personal Information We are given a personal information string S, which may represent eithe ...

  6. Maven魔法堂:安装Oracle JDBC Driver依赖的那些坑

    前言 由于Oracle并没有向公开Maven仓库提供任何Oracle JDBC Driver的Jar包,因此我们无法像MySQL.SQLite等那么轻松直接通过Maven加载依赖. 而手动下载Orac ...

  7. Ideas and Tricks

    1.树上拓扑排序计数 结论$\dfrac{n!}{\prod\limits_{i=1}^n size_i}$ 对于节点$i$,其子树随意排序的结果是$size[i]!$ 但$i$需要排在第一位,只有$ ...

  8. 9、Django之模型层第四篇:进阶操作

    一 QuerySet对象 1.1可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. Entry.objects.all()[:5] # ...

  9. JAVA中使用JSONArray和JSONObject

    json 就是一个键对应一个值,简单的一对一关系. JSONObject  json对象,就是一个键对应一个值(键值对),使用的是大括号{ },如:{key:value} JSONArray  jso ...

  10. 容器场景要选择什么 Linux 版本?

    容器的底层实现深度依赖于内核的众多特性,如 overlay 文件系统,namespace, cgroup 等,因此内核的功能和稳定性,在很大程度上,决定了整个容器PaaS平台的功能和稳定性.从 TKE ...