BZOJ 3673

BZOJ 3674(加强版)

如果每次操作最多只修改一个点的fa[],那么我们可以借助可持久化线段树来O(logn)做到。如果不考虑找fa[]的过程,时空复杂度都是O(logn)。

想要这样就不能加路径压缩,否则要对路径上的点都要改,最好时空复杂度是O(log^2n),但是空间会炸。

合并集合时按秩合并,这样暴力找fa[]的复杂度为O(logn)。

再加上线段树就是O(log^2n)。(当然空间是O(mlogn))

具体:可持久化线段树每个叶子节点储存其fa[x]。每次按秩合并时,在之前树根的基础上新建logn个点,修改对应位置的fa[rt]=new_fa。

按sz启发式合并更好写,而且sz[]还有其它用。3643比按秩合并慢点,3644一样,洛谷上就快些。。

但都是1个log的。


离线做法:操作的版本能构成一棵树。建出树来DFS,进入一个节点时修改,访问完一个节点时撤销修改。


BZOJ 3673:

//6492kb	36ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 600000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=2e4+5,M=2e4+5; int n,root[M];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define S M*16
#define lson son[x][0]
#define rson son[x][1]
int tot,fa[S],dep[S],son[S][2]; void Build(int &x,int l,int r)
{
x=++tot;
if(l==r) fa[x]=l;
else Build(lson,l,l+r>>1),Build(rson,(l+r>>1)+1,r);
}
void Add(int x,int l,int r,int p)//合并两个dep相同的集合时,使没被合并的那个dep+1
{
if(l==r) return (void)++dep[x];
int m=l+r>>1;
p<=m?Add(lson,l,m,p):Add(rson,m+1,r,p);
}
void Modify(int &x,int y,int l,int r,int pos,int v)//重建一遍 将pos处的fa改为v
{
x=++tot;
if(l==r) {fa[x]=v, dep[x]=dep[y]; return;}
int m=l+r>>1;
if(pos<=m) rson=son[y][1], Modify(lson,son[y][0],l,m,pos,v);
else lson=son[y][0], Modify(rson,son[y][1],m+1,r,pos,v);
}
int Query(int x,int l,int r,int pos)
{
if(l==r) return x;//返回树上一个节点(dep是节点的)
int m=l+r>>1;
return pos<=m?Query(lson,l,m,pos):Query(rson,m+1,r,pos);
}
int Get_fa(int rt,int x)
{
int p=Query(rt,1,n,x);//找到fa[p]==x的树上节点p(x是位置...)
return x==fa[p]?p:Get_fa(rt,fa[p]);
}
void Union(int &rt,int x,int y)
{
int p1=Get_fa(rt,x),p2=Get_fa(rt,y);
if(fa[p1]==fa[p2]) return;
if(dep[p1]>dep[p2]) std::swap(p1,p2);//r1->r2
Modify(rt,rt,1,n,fa[p1],fa[p2]);//fa[r1]=r2 -> fa[fa[p1]]=fa[p2]
if(dep[p1]==dep[p2]) Add(rt,1,n,fa[p2]);
}
}T; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
} int main()
{
n=read(), T.Build(root[0],1,n);
for(int m=read(),i=1,opt; i<=m; ++i)
{
if((opt=read())==1) root[i]=root[i-1], T.Union(root[i],read(),read());
else if(opt==2) root[i]=root[read()];
else root[i]=root[i-1], puts(T.fa[T.Get_fa(root[i],read())]==T.fa[T.Get_fa(root[i],read())]?"1":"0");
}
return 0;
}

BZOJ 3674:

//67820kb	724ms
//为啥我的空间开M*20就RE呢。。不管了反正也是跑到前10了。
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 600000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=2e5+5,M=2e5+5; int n,root[M];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define S M*21
#define lson son[x][0]
#define rson son[x][1]
int tot,fa[S],dep[S],son[S][2]; void Build(int &x,int l,int r)
{
x=++tot;
if(l==r) fa[x]=l;
else Build(lson,l,l+r>>1),Build(rson,(l+r>>1)+1,r);
}
void Add(int x,int l,int r,int p)//合并两个dep相同的集合时,使没被合并的那个dep+1
{
if(l==r) return (void)++dep[x];
int m=l+r>>1;
p<=m?Add(lson,l,m,p):Add(rson,m+1,r,p);
}
void Modify(int &x,int y,int l,int r,int pos,int v)//重建一遍 将pos处的fa改为v
{
x=++tot;
if(l==r) {fa[x]=v, dep[x]=dep[y]; return;}
int m=l+r>>1;
if(pos<=m) rson=son[y][1], Modify(lson,son[y][0],l,m,pos,v);
else lson=son[y][0], Modify(rson,son[y][1],m+1,r,pos,v);
}
int Query(int x,int l,int r,int pos)
{
if(l==r) return x;//返回树上一个节点(dep是节点的)
int m=l+r>>1;
return pos<=m?Query(lson,l,m,pos):Query(rson,m+1,r,pos);
}
int Get_fa(int rt,int x)
{
int p=Query(rt,1,n,x);//找到fa[p]==x的树上节点p(x是位置...)
return x==fa[p]?p:Get_fa(rt,fa[p]);
}
void Union(int &rt,int x,int y)
{
int p1=Get_fa(rt,x),p2=Get_fa(rt,y);
if(fa[p1]==fa[p2]) return;
if(dep[p1]>dep[p2]) std::swap(p1,p2);//r1->r2
Modify(rt,rt,1,n,fa[p1],fa[p2]);//fa[r1]=r2 -> fa[fa[p1]]=fa[p2]
if(dep[p1]==dep[p2]) Add(rt,1,n,fa[p2]);
}
}T; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
} int main()
{
n=read(), T.Build(root[0],1,n);
for(int m=read(),i=1,opt,ans=0; i<=m; ++i)
{
if((opt=read())==1) root[i]=root[i-1], T.Union(root[i],read()^ans,read()^ans);
else if(opt==2) root[i]=root[read()^ans];
else root[i]=root[i-1], printf("%d\n",ans=(T.fa[T.Get_fa(root[i],read()^ans)]==T.fa[T.Get_fa(root[i],read()^ans)]));
}
return 0;
}

启发式合并:

//6492kb	56ms
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 600000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=2e4+5,M=2e4+5; int n,root[M];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define S M*16
#define lson son[x][0]
#define rson son[x][1]
int tot,fa[S],sz[S],son[S][2]; void Build(int &x,int l,int r)
{
x=++tot;
if(l==r) fa[x]=l, sz[x]=1;
else Build(lson,l,l+r>>1),Build(rson,(l+r>>1)+1,r);
}
void Modify(int &x,int y,int l,int r,int pos,int v)//重建一遍 将pos处的fa改为v
{
x=++tot;
if(l==r) {fa[x]=v, sz[x]=sz[y]; return;}
int m=l+r>>1;
if(pos<=m) rson=son[y][1], Modify(lson,son[y][0],l,m,pos,v);
else lson=son[y][0], Modify(rson,son[y][1],m+1,r,pos,v);
}
int Query(int x,int l,int r,int pos)
{
if(l==r) return x;//返回树上一个节点(dep是节点的)
int m=l+r>>1;
return pos<=m?Query(lson,l,m,pos):Query(rson,m+1,r,pos);
}
int Get_fa(int rt,int x)
{
int p=Query(rt,1,n,x);//找到fa[p]==x的树上节点p(x是位置...)
return x==fa[p]?p:Get_fa(rt,fa[p]);
}
void Union(int &rt,int x,int y)
{
int p1=Get_fa(rt,x),p2=Get_fa(rt,y);
if(fa[p1]==fa[p2]) return;
if(sz[p1]>sz[p2]) std::swap(p1,p2);//r1->r2
sz[p2]+=sz[p1];
Modify(rt,rt,1,n,fa[p1],fa[p2]);//fa[r1]=r2 -> fa[fa[p1]]=fa[p2]
}
}T; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
} int main()
{
n=read(), T.Build(root[0],1,n);
for(int m=read(),i=1,opt; i<=m; ++i)
{
if((opt=read())==1) root[i]=root[i-1], T.Union(root[i],read(),read());
else if(opt==2) root[i]=root[read()];
else root[i]=root[i-1], puts(T.fa[T.Get_fa(root[i],read())]==T.fa[T.Get_fa(root[i],read())]?"1":"0");
}
return 0;
}

BZOJ.3673/3674.可持久化并查集(可持久化线段树 按秩合并/启发式合并)的更多相关文章

  1. 【bzoj4399】魔法少女LJJ 并查集+权值线段树合并

    题目描述 在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新.淡雅,到处散发着醉人的奶浆味: ...

  2. [bzoj] 3673 3674 可持久化并查集 || 可持久化数组

    原题 加强版 题意: 可持久化并查集模板-- 题解: 用可持久化线段树维护一个可持久化数组,来记录每一次操作后的状态. 不能用路径压缩,但是要按置合并,使复杂度保证在O(log) #include&l ...

  3. BZOJ 3674 可持久化并查集加强版(主席树变形)

    3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MB Submit: 2515  Solved: 1107 [Submit][Sta ...

  4. Bzoj 3673: 可持久化并查集 by zky(主席树+启发式合并)

    3673: 可持久化并查集 by zky Time Limit: 5 Sec Memory Limit: 128 MB Description n个集合 m个操作 操作: 1 a b 合并a,b所在集 ...

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

    3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1878  Solved: 846[Submit][Status ...

  6. 【BZOJ3673/3674】可持久化并查集/可持久化并查集加强版 可持久化线段树

    [BZOJ3674]可持久化并查集加强版 Description Description:自从zkysb出了可持久化并查集后……hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了! ...

  7. 2019.01.21 bzoj3674: 可持久化并查集加强版(主席树+并查集)

    传送门 题意:维护可持久化并查集,支持在某个版本连边,回到某个版本,在某个版本 询问连通性. 思路: 我们用主席树维护并查集fafafa数组,由于要查询历史版本,因此不能够用路径压缩. 可以考虑另外一 ...

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

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

  9. bzoj 2733: [HNOI2012]永无乡【并查集+权值线段树】

    bzoj上数组开大会T-- 本来想用set瞎搞的,想了想发现不行 总之就是并查集,每个点开一个动态开点的权值线段树,然后合并的时候把值并在根上,询问的时候找出在根的线段树里找出k小值,看看这个值属于哪 ...

随机推荐

  1. Nodejs stream模块-翻译

    花了两天时间尝试按照自己的话翻译了一下stream模块,以下内容皆翻译于:https://nodejs.org/api/stream.html. 目录 1  Stream(流)     1.1     ...

  2. Redis五种数据结构(Windows Server)

    1.Redis的五种数据结构 这里推荐大家在命名redis的key的时候最好的加上前缀,并且使用 :来分割前缀 ,这里在使用可视化工具查看的时候就比较好区分,比如我的的前缀是 Demo:test:(一 ...

  3. [整理]win7下VS2010遇到内存不足解决方发

    电脑重装Win7 64bit不久后,一天内VS2010使用久了,就会出现内存不足,实际内存使用情况却不是,显示内存已使用70%.以前没有遇到过,经网上查找,貌似是VS2010对内存计算会在某些情况下计 ...

  4. AngularJS入门基础——$provide.decorator 实例讲解

    <body ng-controller="OneController"> <script>  var Foobar = function() {      ...

  5. 视觉中的经典图像特征小结(一): 颜色直方图, HOG, LBP

    [普兒原创, 如有错误和纰漏欢迎指正. 更新中...] 1. 颜色直方图 颜色空间在本质上是定义在某种坐标系统下的子空间,空间中的每一个坐标表示一种不同的颜色.颜色空间的目的在于给出某种颜色标准,使得 ...

  6. 『Numpy学习指南』Matplotlib绘图

    数据生成: import numpy as np import matplotlib.pyplot as plt func = np.poly1d(np.array([,,,])) func1 = f ...

  7. VS 2013 中如何自定义快捷键(图解)

    随着软件的升级其功能也越来越多,所以相应的快捷键也多了起来.VS2013中的快捷方式已经发展为了两个快捷键的组合.例如VS2013中默认的多行注释的快捷功能键为(ctrl+k,ctrl+u).像这样类 ...

  8. 001_软件waf

    一.优秀的软件waf开源软件 <1>openwaf介绍  http://www.oschina.net/p/openwaf http://git.oschina.net/miracleqi ...

  9. 一个无锁消息队列引发的血案(三)——地:q3.h 与 RingBuffer

    目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...

  10. django-xadmin后台开发

    先通过pip命令行安装django<=1.9版本 示例:pip install django==1.9 从https://github.com/sshwsfc/xadmin下载xadmin源码解 ...