【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集

3673: 可持久化并查集 by zky

Time Limit: 5 Sec  Memory Limit: 128 MB
Submit: 1878  Solved: 846
[Submit][Status][Discuss]

Description

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

0<n,m<=2*10^4

Input

Output

Sample Input

5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2

Sample Output

1
0
1

HINT

Source

出题人大SB

3674: 可持久化并查集加强版

Time Limit: 15 Sec  Memory Limit: 256 MB
Submit: 2367  Solved: 886
[Submit][Status][Discuss]

Description

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<n,m<=2*10^5

Input

Output

Sample Input

5 6
1 1 2
3 1 2
2 1
3 0 3
2 1
3 1 2

Sample Output

1
0
1

HINT

Source

出题人大SB++

Solution

先说离线的一种思路,以前yveh做Codeforces的时候告诉我一道题,与之类似,存在一个状态退回到第多少步,当时是需要线段树维护一个东西,所以可以形成一个树形结构,然后dfs并回溯离线做。

但是这里并不能那么搞,因为并查集不支持删除所以没法回溯,所以只能考虑令并查集的 代表元素数组可持久化

所以本质上都得用可持久化数据结构维护代表元素数组,就无所谓离线在线了。

具体的做法就是建可持久化线段树,每次修改就在线段树上建一个新的链,叶子节点维护的是这个点的代表元素,所以方法很简单就是先初始化建出一棵新的树,然后每次新建一条链即可。

然后这里就剩下一个问题了,就是并查集$find$,为了保证复杂度,我采用了路径压缩但这里发现路径压缩会使树上操作数增多,建很多新的节点,所以常数比较大,然后看了hxy和zyf的代码,发现她们都和hzwer写的一样,采用的按秩合并,不过常数比我还大,不过显而易见 如果不加优化一定会TLE,所以二选一即可,并无太大的差距。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define MAXN 500010
int N,M,last;
namespace PrTree
{
    int sz,lson[MAXN*20],rson[MAXN*20],root[MAXN],f[MAXN*20];
    inline void Insert(int l,int r,int &now,int fa,int pos,int val)
    {
        now=++sz;
        if (l==r) {f[now]=val; return;}
        int mid=(l+r)>>1;
        lson[now]=lson[fa],rson[now]=rson[fa];
        if (pos<=mid) Insert(l,mid,lson[now],lson[fa],pos,val);
            else Insert(mid+1,r,rson[now],rson[fa],pos,val);
    }
    inline int Query(int l,int r,int pos,int now)
    {
        if (l==r) return f[now];
        int mid=(l+r)>>1;
        if (pos<=mid) return Query(l,mid,pos,lson[now]);
            else return Query(mid+1,r,pos,rson[now]);
    }
    inline void BuildTree(int l,int r,int &now)
    {
        now=++sz;
        if (l==r) {f[now]=l; return;}
        int mid=(l+r)>>1;
        BuildTree(l,mid,lson[now]); BuildTree(mid+1,r,rson[now]);
    }
}using namespace PrTree;
inline int F(int x,int &rt) {int fa; if ((fa=Query(1,N,x,rt))==x) return x; else return Insert(1,N,rt,rt,x,fa=F(fa,rt)),fa;}
inline void Merge(int x,int y,int &rt) {int fx=F(x,rt),fy=F(y,rt); if (fx!=fy) Insert(1,N,rt,rt,fx,fy);}
int main()
{
    N=read(),M=read();
    BuildTree(1,N,root[0]);
    for (int i=1; i<=M; i++)
        {
            root[i]=root[i-1];
            int opt=read(),x,y;
            switch (opt)
                {
                    case 1: x=read(),y=read(); x^=last,y^=last; Merge(x,y,root[i]); break;
                    case 2: x=read(); x^=last; root[i]=root[x]; break;
                    case 3: x=read(),y=read(); x^=last,y^=last; printf("%d\n",last=(F(x,root[i])==F(y,root[i]))); break;
                }
        }
    return 0;
}

总体来说写起来还是蛮好写的,就是自己开始手误,在可持久化线段树的时候 左右都下放的lson,然后瞪着屏幕20分钟才看出来...

时间: 2024-10-17 09:57:45

【BZOJ-3673&3674】可持久化并查集 可持久化线段树 + 并查集的相关文章

UVALive 4730 Kingdom 线段树+并查集

题目链接:点击打开链接 题意见白书P248 思路: 先把读入的y值都扩大2倍变成整数 然后离散化一下 用线段树来维护y轴 区间上每个点的 城市数量和联通块数量, 然后用并查集维护每个联通块及联通块的最大最小y值,还要加并查集的秩来记录每个联通块的点数 然后就是模拟搞.. T^T绝杀失败题..似乎数组开小了一点就过了,== #include<stdio.h> #include<math.h> #include<vector> #include<string.h>

UVA 1455 - Kingdom(线段树+并查集)

UVA 1455 - Kingdom 题目链接 题意:给定一些城市坐标点,连在一起的城市称为一个州,现在用两种操作,road表示把城市a,b建一条路,line表示询问一个y轴上穿过多少个州,和这些州共包含多少个城市 思路:利用并查集维护每个州的上界和下界还有城市个数,然后每次加进一条路的时候,根据两个集合的位置可以处理出区间的州和城市数如何进行加减,然后利用线段树搞就可以了 代码: #include <cstdio> #include <cstring> #include <

【BZOJ 4662】 4662: Snow (线段树+并查集)

4662: Snow Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 136  Solved: 47 Description 2333年的某一天,临冬突降大雪,主干道已经被雪覆盖不能使用.城 主 囧·雪 决定要对主干道进行一次清扫. 临冬城的主干道可以看为一条数轴.囧·雪 一共找来了n个清理工,第 i个清理工的工作范围为[li,ri],也就是说这个清理工会把[li,ri]这一 段主干道清理干净(当然已经被清理过的部分就被忽略了).当然有可能主 干道

(线段树+并查集) Codeforces Round #416 (Div. 2) E Vladik and Entertaining Flags

In his spare time Vladik estimates beauty of the flags. Every flag could be represented as the matrix n?×?m which consists of positive integers. Let's define the beauty of the flag as number of components in its matrix. We call component a set of cel

UVALive 4730 线段树+并查集

点击打开链接 题意:在坐标上给n个点,r的操作是将两个点连起来,l的操作是问你y=u的这条线连接的集合块数和这些集合内的点的个数 思路:很麻烦的一道题,在网上看了题意和做法后,开始了一下午的调bug过程,做法很好懂,我开了两个线段树,一个维护点代表的直线的集合个数,另一个则是路过集合内的点的个数,然后集合的判断直接用并查集就行了,这是两个核心,然后就是自己瞎写的了,代码丑的可以而且好像除了本人别人看着可能要骂人了,有兴趣研究的可以留言我来解答,那难的部分其实就是并查集合并时该怎么将这两个要维护的

【Codeforces811E】Vladik and Entertaining Flags [线段树][并查集]

Vladik and Entertaining Flags Time Limit: 20 Sec  Memory Limit: 512 MB Description n * m的矩形,每个格子上有一个数字代表颜色. q次询问,询问[l, r]有几个连通块,若颜色相同并且连通则属于同一个连通块. Input 输入第一行n,m,q. 然后一个n*m的矩形. 之后q行,每行两个整数l,r. Output 输出q行,对于每个询问输出答案. Sample Input 4 5 4 1 1 1 1 1 1 2

【XSY2707】snow 线段树 并查集

题目描述 有\(n\)个人和一条长度为\(t\)的线段,每个人还有一个工作范围(是一个区间).最开始整条线段都是白的.定义每个人的工作长度是这个人的工作范围中白色部分的长度(会随着线段改变而改变).每一天开始时你要选择一个人满足这个人的工作长度最小(如果有多个就选编号最小的).把这个人的工作区间染黑.请你输出每天你选了哪个人. 保证工作范围中左端点和右端点单调递增. \(n\leq 300000\) 题解 先把线段离散化成很多个小区间,那么每个小区间只会被染黑一次(染黑之后不会变白). 因此每次

【CF687D】Dividing Kingdom II 线段树+并查集

[CF687D]Dividing Kingdom II 题意:给你一张n个点m条边的无向图,边有边权$w_i$.有q个询问,每次给出l r,问你:如果只保留编号在[l,r]中的边,你需要将所有点分成两个集合,使得这个划分的代价最小,问最小代价是什么.一个划分的代价是指,对于所有两端点在同一集合中的边,这些边的边权最大值.如果没有端点在同一集合中的边,则输出-1. $n,q\le 1000,m\le \frac {n(n-1)} 2,w_i\le 10^9$ 题解:先考虑暴力的做法,我们将所有边按

[CSP-S模拟测试]:Dash Speed(线段树+并查集+LCA)

题目描述 比特山是比特镇的飙车圣地.在比特山上一共有$n$个广场,编号依次为$1$到$n$,这些广场之间通过$n−1$条双向车道直接或间接地连接在一起,形成了一棵树的结构. 因为每条车道的修建时间以及建筑材料都不尽相同,所以可以用两个数字$l_i,r_i$量化地表示一条车道的承受区间,只有当汽车以不小于$l_i$且不大于$r_i$的速度经过这条车道时,才不会对路面造成伤害. $Byteasar$最近新买了一辆跑车,他想在比特山飙一次车.$Byteasar$计划选择两个不同的点$S,T$,然后在它