热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

BZOJ3673&BZOJ3674可持续化并查集【可持续化线段树维护可持续化数组】

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

题目描述

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
#include
#include
#include
#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
#include
#include
#include
#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;
}

推荐阅读
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
author-avatar
沈畅棉多多_574
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有