BZOJ3673 & BZOJ3674 可持续化并查集 【可持续化线段树维护可持续化数组】
题目描述
n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
0
输入格式
输出格式
输入样例
5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
输出样例
1
0
1
题解
这道题要维护可持续化并查集,由于并查集是由数组实现的,所以实质是维护并查集的pre数组
路径压缩怎么办?实际上可以按轶合并,轶指最深的深度
每次合并集合时,将轶小的并到轶大的,当二者相等,被并的轶+1,即最大深度+1
这样子维护的并查集近似于完全二叉树,可以做到查询均摊O(logn)
由于没怎么写过可持续化数组,这里讲一讲:
可持续化数组,实际上就是可持续化线段树。可以看做废掉了中间节点的主席树,每次修改和查询都一样,无论是空间还是时间都是O(logn)
我们先开一个0版本线段树,每个叶子节点有一个值,表示对应位置的数组的值
每次修改,加一个版本的根,然后让新版本的树沿着上一版本创建。有修改的那一条路径新开节点,剩余的子树指向原版本【因为本来就一样】
每次询问,只需找到对应版本的根,往叶子查找即可
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
using namespace std;
const int maxn = 20005,maxm = 2000005,INF = 1000000000;
inline int RD(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
return out * flag;
}
int N,M,siz = 0,rt[maxn],ri = 0;
struct node{int ls,rs,fa,dep;}e[maxm];
void build(int& u,int l,int r){
u = ++siz;
if (l == r){e[u].fa = l; return;}
int mid = l + r >> 1;
build(e[u].ls,l,mid);
build(e[u].rs,mid + 1,r);
}
int Query(int u,int l,int r,int pos){
if (l == r) return u;
int mid = l + r >> 1;
if (mid >= pos) return Query(e[u].ls,l,mid,pos);
else return Query(e[u].rs,mid + 1,r,pos);
}
void modify(int& u,int pre,int l,int r,int pos,int val){
e[u = ++siz] = e[pre];
if (l == r) {e[u].fa = val; return;}
int mid = l + r >> 1;
if (mid >= pos) modify(e[u].ls,e[pre].ls,l,mid,pos,val);
else modify(e[u].rs,e[pre].rs,mid + 1,r,pos,val);
}
void add(int u,int l,int r,int pos){
if (l == r) {e[u].dep++; return;}
int mid = l + r >> 1;
if (mid >= pos) add(e[u].ls,l,mid,pos);
else add(e[u].rs,mid + 1,r,pos);
}
int find(int R,int u){
int p = Query(R,1,N,u);
if (e[p].fa == u) return p;
return find(R,e[p].fa);
}
int main(){
N = RD(); M = RD(); int cmd,a,b,fa,fb;
build(rt[0],1,N);
REP(i,M){
cmd = RD(); a = RD(); ri++;
if (cmd == 1){
b = RD(); rt[i] = rt[i - 1];
fa = find(rt[i],a); fb = find(rt[i],b);
if (e[fa].fa != e[fb].fa){
if (e[fa].dep > e[fb].dep) swap(fa,fb);
modify(rt[ri],rt[ri - 1],1,N,e[fa].fa,e[fb].fa);
if (e[fa].dep == e[fb].dep) add(rt[ri],1,N,e[fb].fa);
}
}else if (cmd == 2){
rt[ri] = rt[a];
}else {
b = RD(); rt[ri] = rt[ri - 1];
fa = find(rt[ri],a); fb = find(rt[ri],b);
printf("%d\n",fa == fb);
}
}
return 0;
}
题目描述
Description:
自从zkysb出了可持久化并查集后……
hzwer:乱写能AC,暴力踩标程
KuribohG:我不路径压缩就过了!
ndsf:暴力就可以轻松虐!
zky:……
n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
请注意本题采用强制在线,所给的a,b,k均经过加密,加密方法为x = x xor lastans,lastans的初始值为0
0
输入格式
输出格式
输入样例
5 6
1 1 2
3 1 2
2 1
3 0 3
2 1
3 1 2
输出样例
1
0
1
题解
实际是一样的,O(nlog2n)的复杂度怎么卡得掉
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
using namespace std;
const int maxn = 200005,maxm = 10000005,INF = 1000000000;
inline int RD(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
return out * flag;
}
int N,M,siz = 0,rt[maxn],ri = 0;
struct node{int ls,rs,v,dep;}e[maxm];
void build(int& u,int l,int r){
if (!u) u = ++siz;
if (l == r){e[u].v = l; return;}
int mid = l + r >> 1;
build(e[u].ls,l,mid);
build(e[u].rs,mid + 1,r);
}
int Query(int u,int l,int r,int pos){
if (l == r) return u;
int mid = l + r >> 1;
if (mid >= pos) return Query(e[u].ls,l,mid,pos);
else return Query(e[u].rs,mid + 1,r,pos);
}
void modify(int& u,int pre,int l,int r,int pos,int val){
u = ++siz;
if (l == r) {e[u].v = val; e[u].dep = e[pre].dep; return;}
e[u].ls = e[pre].ls; e[u].rs = e[pre].rs;
int mid = l + r >> 1;
if (mid >= pos) modify(e[u].ls,e[pre].ls,l,mid,pos,val);
else modify(e[u].rs,e[pre].rs,mid + 1,r,pos,val);
}
void add(int u,int l,int r,int pos){
if (l == r) {e[u].dep++; return;}
int mid = l + r >> 1;
if (mid >= pos) add(e[u].ls,l,mid,pos);
else add(e[u].rs,mid + 1,r,pos);
}
int find(int R,int u){
int p = Query(R,1,N,u);
if (e[p].v == u) return p;
return find(R,e[p].v);
}
int main(){
N = RD(); M = RD(); int cmd,a,b,p,q,last = 0;
build(rt[0],1,N);
REP(i,M){
cmd = RD(); a = RD() ^ last; ri++;
if (cmd == 1){
b = RD() ^ last; rt[i] = rt[i - 1];
p = find(rt[i],a); q = find(rt[i],b);
if (e[p].v != e[q].v){
if (e[p].dep > e[q].dep) swap(p,q);
modify(rt[ri],rt[ri - 1],1,N,e[p].v,e[q].v);
if (e[p].dep == e[q].dep) add(rt[ri],1,N,e[q].v);
}
}else if (cmd == 2){
rt[ri] = rt[a];
}else {
b = RD() ^ last; rt[ri] = rt[ri - 1];
p = find(rt[ri],a); q = find(rt[ri],b);
if (e[p].v == e[q].v) last = 1;
else last = 0;
printf("%d\n",last);
}
}
return 0;
}
BZOJ3673 & BZOJ3674 可持续化并查集 【可持续化线段树维护可持续化数组】的更多相关文章
- 【BZOJ2054】疯狂的馒头(并查集,线段树)
[BZOJ2054]疯狂的馒头(并查集,线段树) 题面 BZOJ 然而权限题,随便找个离线题库看看题吧. 题解 线段树就是个暴力,如果数据可以构造就能卡掉,然而不能构造,要不然复杂度瓶颈成为了读入了. ...
- UVALive - 5031 Graph and Queries (并查集+平衡树/线段树)
给定一个图,支持三种操作: 1.删除一条边 2.查询与x结点相连的第k大的结点 3.修改x结点的权值 解法:离线倒序操作,平衡树or线段树维护连通块中的所有结点信息,加个合并操作就行了. 感觉线段树要 ...
- POJ 1944 Fiber Communications (枚举 + 并查集 OR 线段树)
题意 在一个有N(1 ≤ N ≤ 1,000)个点环形图上有P(1 ≤ P ≤ 10,000)对点需要连接.连接只能连接环上相邻的点.问至少需要连接几条边. 思路 突破点在于最后的结果一定不是一个环! ...
- ACM学习历程—SNNUOJ 1110 传输网络((并查集 && 离线) || (线段树 && 时间戳))(2015陕西省大学生程序设计竞赛D题)
Description Byteland国家的网络单向传输系统可以被看成是以首都 Bytetown为中心的有向树,一开始只有Bytetown建有基站,所有其他城市的信号都是从Bytetown传输过来的 ...
- 2019牛客第八场多校 E_Explorer 可撤销并查集(栈)+线段树
目录 题意: 分析: @(2019牛客暑期多校训练营(第八场)E_Explorer) 题意: 链接 题目类似:CF366D,Gym101652T 本题给你\(n(100000)\)个点\(m(1000 ...
- [bzoj3673][可持久化并查集 by zky] (rope(可持久化数组)+并查集=可持久化并查集)
Description n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0& ...
- HDU 3974 Assign the task 并查集/图论/线段树
Assign the task Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?p ...
- bzoj3673可持久化并查集 by zky&&bzoj3674可持久化并查集加强版
bzoj3673可持久化并查集 by zky 题意: 维护可以恢复到第k次操作后的并查集. 题解: 用可持久化线段树维护并查集的fa数组和秩(在并查集里的深度),不能路径压缩所以用按秩启发式合并,可以 ...
- bzoj3673可持久化并查集
n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0<n,m<=2*10^ ...
随机推荐
- 让UltraEdit-32成为Delphi 7编译器的工具设置
UltraEdit-32编译Delphi的工具设置 {================================================}Dcc32 命令行(&C):C:\Pro ...
- C语言实例解析精粹学习笔记——43(希尔排序)
实例说明: 用希尔排序方法对数组进行排序.由于书中更关注的实例,对于原理来说有一定的解释,但是对于第一次接触的人来说可能略微有些简略.自己在草稿纸上画了好久,后来发现网上有好多很漂亮的原理图. 下面将 ...
- PAT (Basic Level) Practice 1009 说反话
给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出. 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80 的字符串.字符串由若干单词和若干空格组成,其中单词是由英文字母(大小 ...
- R语言绘图:在地图上绘制热力图
使用ggplot2在地图上绘制热力图 ######*****绘制热力图代码*****####### interval <- seq(0, 150000, 25000)[-2] #设置价格区间 n ...
- 为什么我要放弃javaScript数据结构与算法(第三章)—— 栈
有两种结构类似于数组,但在添加和删除元素时更加可控,它们就是栈和队列. 第三章 栈 栈数据结构 栈是一种遵循后进先出(LIFO)原则的有序集合.新添加的或待删除的元素都保存在栈的同一端,称为栈顶,另一 ...
- linux io 学习笔记(01)---锁,信号量
1.采用信号量访问:当有段临界代码,需要保证排他的访问一个资源. 2.sudo dmesg -c 消除dmesg缓冲 3.互斥锁:代表的是一种锁资源,互斥锁的工作原理是:保证对共享资源操作的原子性 ...
- 【转】iOS库 .a与.framework区别
转自:http://blog.csdn.net/lvxiangan/article/details/43115131 一.什么是库? 库是共享程序代码的方式,一般分为静态库和动态库. 二.静态库与动态 ...
- EIP权限工作流平台总结-1总体说明
预览地址:www.eipflow.com (1) 权限工作流:www.demo.eipflow.com/Account/Login (2) 基础权限版:www.auth.eipflow.com/A ...
- 台湾ML笔记--1.1什么时候适合使用ML
适用情况: 1 exists some 'underlying pattern' to be learned --so 'performance measure' can be imporoved 例 ...
- 『AngularJS』Service
理解Angular 服务 什么是Angular Service Angular 服务是为web应用执行特定任务的单例对象或方法. 注:如果组件是为了内容呈现的功能复用,那么服务就是为组件进行功能复用. ...