【BZOJ3674】可持久化并查集加强版

可持久化并查集我觉得就是可持久化数组的一种应用。可持久化数组,顾名思义,就是有历史版本的数组,那么如果我们暴力修改储存的话,修改O(n)查询O(1),空间O(n*m),这样肯定不可行,那么我们发现主席树有这样的功能,他可以快速复制,修改O(log),查询O(log),空间(m*log),是一个可行的方案。然后我们可持久化f数组维护fa,每次按照深度启发式合并,不进行路径压缩,这样能保证时间复杂度位单次O(log^2),空间复杂度为O(2*n+m*log)。我不知道为什么不路径压缩,路径压缩是完全可以的,但是他的时间复杂度和空间复杂度似乎都不如不路径压缩看,而且似乎并不好打。

#include <cstdio>
using namespace std;
inline void read(int &sum){
  register char ch=getchar();
  for(sum=0;ch<‘0‘||ch>‘9‘;ch=getchar());
  for(;ch>=‘0‘&&ch<=‘9‘;sum=(sum<<1)+(sum<<3)+ch-‘0‘,ch=getchar());
}
const int N=200010;
struct Segment_Tree{
  Segment_Tree *ch[2];int f,deep;
  void* operator new(size_t);
}*root[N],*null,*C,*mempool;
int n,m;
void* Segment_Tree :: operator new(size_t){
  if(C==mempool)C=new Segment_Tree[(1<<15)+10],mempool=C+(1<<15)+10;
  return C++;
}
inline void build(Segment_Tree *&p,int l,int r){
  p=new Segment_Tree,p->deep=p->f=0;
  if(l==r){
    p->f=l,p->deep=1,p->ch[0]=p->ch[1]=null;
    return;
  }
  build(p->ch[0],l,(l+r)>>1);
  build(p->ch[1],((l+r)>>1)+1,r);
}
inline void insert(Segment_Tree *&p,Segment_Tree *last,int pos,int key,int l,int r){
  p=new Segment_Tree,*p=*last;
  if(l==r){
    p->f=key;return;
  }
  if(pos<=((l+r)>>1))insert(p->ch[0],last->ch[0],pos,key,l,((l+r)>>1));
  else insert(p->ch[1],last->ch[1],pos,key,((l+r)>>1)+1,r);
}
inline void update(Segment_Tree *p,int pos){
  register int l=1,r=n;
  while(l!=r){
    if(pos<=((l+r)>>1)){
      p=p->ch[0],r=((l+r)>>1);
    }else{
      p=p->ch[1],l=((l+r)>>1)+1;
    }
  }
  p->deep++;
}
inline int deep(Segment_Tree *p,int pos){
  register int l=1,r=n;
  while(l!=r){
    if(pos<=((l+r)>>1)){
      p=p->ch[0],r=((l+r)>>1);
    }else{
      p=p->ch[1],l=((l+r)>>1)+1;
    }
  }
  return p->deep;
}
inline int F(Segment_Tree *p,int pos){
  register int l=1,r=n;
  while(l!=r){
    if(pos<=((l+r)>>1)){
      p=p->ch[0],r=((l+r)>>1);
    }else{
      p=p->ch[1],l=((l+r)>>1)+1;
    }
  }
  return p->f;
}
inline int find_root(Segment_Tree *p,int x){
  int fa=F(p,x);
  return fa==x?x:find_root(p,fa);
}
inline void Init(){
  null=new Segment_Tree,null->ch[0]=null->ch[1]=null,null->f=0,null->deep=0;
  read(n),read(m);
  build(root[0],1,n);
}
inline void Work(){
  register int ans=0,opt,a,b,x,y,d1,d2;
  for(register int i=1;i<=m;i++){
    read(opt);
    switch(opt){
      case 1:read(a),a^=ans,read(b),b^=ans;
            x=find_root(root[i-1],a),y=find_root(root[i-1],b);
            if(x==y){
              root[i]=root[i-1];
              break;
            }
            d1=deep(root[i-1],x),d2=deep(root[i-1],y);
            if(d1>d2)x^=y^=x^=y;insert(root[i],root[i-1],x,y,1,n);
            if(d1==d2)update(root[i],y);
            break;
      case 2:read(a),a^=ans;
            root[i]=root[a];break;
      case 3:read(a),read(b),a^=ans,b^=ans;
            root[i]=root[i-1];
            x=find_root(root[i],a),y=find_root(root[i],b);
            if(x==y)puts("1"),ans=1;
            else puts("0"),ans=0;
            break;
    }
  }
}
int main(){
  Init();
  Work();
  return 0;
}
时间: 2024-08-26 08:42:26

【BZOJ3674】可持久化并查集加强版的相关文章

bzoj3673可持久化并查集 by zky&amp;&amp;bzoj3674可持久化并查集加强版

bzoj3673可持久化并查集 by zky 题意: 维护可以恢复到第k次操作后的并查集. 题解: 用可持久化线段树维护并查集的fa数组和秩(在并查集里的深度),不能路径压缩所以用按秩启发式合并,可以使合并均摊复杂度为O(nlog2n).可持久化线段树实际上就是在更新节点时按主席树的插入方式新建一条路径(其实主席树就是可持久化权值线段树). 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm&g

bzoj3674 可持久化并查集加强版

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的初始值为00<n,m<=2*10^5 Samp

[BZOJ3674]可持久化并查集加强版&amp;[BZOJ3673]可持久化并查集 by zky

思路: 用主席树维护并查集森林,每次连接时新增结点. 似乎并不需要启发式合并,我随随便便写了一个就跑到了3674第一页?3673是这题的弱化版,本来写个暴力就能过,现在借用加强版的代码(去掉异或),直接吊打暴力程序. 1 #include<cstdio> 2 #include<cctype> 3 inline int getint() { 4 register char ch; 5 while(!isdigit(ch=getchar())); 6 register int x=ch

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

http://www.lydsy.com/JudgeOnline/problem.php?id=3674 3674: 可持久化并查集加强版 Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 3225  Solved: 1192[Submit][Status][Discuss] Description Description:自从zkysb出了可持久化并查集后--hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可

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

3674: 可持久化并查集加强版 Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 2605  Solved: 977[Submit][Status][Discuss] Description Description:自从zkysb出了可持久化并查集后……hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可以轻松虐!zky:…… n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状

bzoj3674: 可持久化并查集

用可持久化线段树维护可持久化并查集. 调了一下午,改为按秩合并就过了... #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=200100; const int INF=1e9+10; int n,m; int fa[ma

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

Time Limit: 15 Sec  Memory Limit: 256 MBSubmit: 3592  Solved: 1337[Submit][Status][Discuss] Description Description:自从zkysb出了可持久化并查集后……hzwer:乱写能AC,暴力踩标程KuribohG:我不路径压缩就过了!ndsf:暴力就可以轻松虐!zky:…… n个集合 m个操作操作:1 a b 合并a,b所在集合2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询

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

3673: 可持久化并查集 by zky Time Limit: 5 Sec  Memory Limit: 128 MB Submit: 2515  Solved: 1107 [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 Inp

【BZOJ】3673: 可持久化并查集 by zky &amp; 3674: 可持久化并查集加强版(可持久化线段树)

http://www.lydsy.com/JudgeOnline/problem.php?id=3674 http://www.lydsy.com/JudgeOnline/problem.php?id=3673 双倍经验啦啦啦.. 给主席树换了个名称果然高大上... 首先要可持久化并查集其实就是可持久化数组... 那么因为数组的形式是这样的$P[x]$,那么我们用一种数据结构实现查找x返回对应的$P[x]$即可啦啦啦. 然后那么我所学的可持久化目前只有主席树QAQ哪天去写写fhqtreap...