板子稳点,心里总是会比较放心的,这题我wa了十几发后成功对着别人的代码找到了问题,并查集按秩合并里面我把
rank[x]++
写错了modify(int l,int r,rootrank[ver-1],root[ver],x,depx+1)
,本来应该这样写,我把下标给弄错了,真难受
题解:
可持久化并查集需要用到按秩合并的方法写,不能用路径压缩,首先需要建两个主席树,一个主席树存集合祖先,一个主席树存集合的秩,找祖宗的写法和查集差不多,首先我们需要知道当前版本pos的祖先,然后判断x和它的祖先是不是同一个,直到找到pos的祖先是谁即可 合并利用按秩合并,主席树的查询操作就相当于普通并查集中的
pre[ ]
和rank[ ]
数组,修改就在另一个版本上进行修改即可
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int rootpre[maxn],rootrank[maxn];
struct node
{
int l,r,val;
} tr[maxn*2*40];
int cnt,tot,n;
void build(int l,int r,int &now)//初始化pre数组
{
now=++cnt;
if(l==r)
{
tr[now].val=l;
return ;
}
int m=l+r>>1;
build(l,m,tr[now].l);
build(m+1,r,tr[now].r);
}
void modify(int l,int r,int ver,int &now,int pos,int w)
{
now=++cnt;
tr[now]=tr[ver];
if(l==r)
{
tr[now].val=w;
return ;
}
int m=l+r>>1;
if(m>=pos)
{
modify(l,m,tr[ver].l,tr[now].l,pos,w);
}
else
{
modify(m+1,r,tr[ver].r,tr[now].r,pos,w);
}
}
int query(int l,int r,int ver,int pos)
{
if(l==r)
{
return tr[ver].val;
}
int m=l+r>>1;
if(m>=pos)
{
return query(l,m,tr[ver].l,pos);
}
else
{
return query(m+1,r,tr[ver].r,pos);
}
}
int findx(int ver,int x)
{
int fa=query(1,n,rootpre[ver],x);
if(fa!=x)
{
fa=findx(ver,fa);
}
return fa;
}
void mere(int ver,int x,int y)
{
int fx=findx(ver-1,x);
int fy=findx(ver-1,y);
if(fy==fx)
{
rootpre[ver]=rootpre[ver-1];
rootrank[ver]=rootrank[ver-1];
}
else
{
int depx=query(1,n,rootrank[ver-1],fx);
int depy=query(1,n,rootrank[ver-1],fy);
if(depx<depy)
{
modify(1,n,rootpre[ver-1],rootpre[ver],fx,fy);
rootrank[ver]=rootrank[ver-1];
}
else if(depx>depy)
{
modify(1,n,rootpre[ver-1],rootpre[ver],fy,fx);
rootrank[ver]=rootrank[ver-1];
}
else
{
modify(1,n,rootpre[ver-1],rootpre[ver],fx,fy);
modify(1,n,rootrank[ver-1],rootrank[ver],fy,depy+1);
}
}
}
int main()
{
int m;
scanf("%d %d",&n,&m);
build(1,n,rootpre[0]);
int id=0;
for(int ver=1; ver<=m; ver++)
{
int opt,a,b;
scanf("%d",&opt);
if(opt==1)
{
scanf("%d %d",&a,&b);
mere(ver,a,b);
}
else if(opt==2)
{
int x;
scanf("%d",&x);
rootpre[ver]=rootpre[x];
rootrank[ver]=rootrank[x];
}
else
{
scanf("%d %d",&a,&b);
rootpre[ver]=rootpre[ver-1];
rootrank[ver]=rootrank[ver-1];
int fx=findx(ver,a);
int fy=findx(ver,b);
if(fx==fy)
{
printf("1\n");
}
else
printf("0\n");
}
}
}
手撕代码:
#include<bits/stdc++.h>
using namespace std;
//可持久化并查集
//按秩合并,不改变树的整体结构
const int maxn=2e5+5;
struct node
{
int l,r,val;
}tr[maxn*40*2];//开两颗主席树
int n,cnt;//计数器
int root[maxn],ran[maxn];//集合数组,树高数组
//可持久化数组
void build(int l,int r,int &now)//初始化
{
now=++cnt;
if(l==r)
{
tr[now].val=l;
return;
}
int m=l+r>>1;
build(l,m,tr[now].l);
build(m+1,r,tr[now].r);
}
void modify(int l,int r,int ver,int &now,int pos,int w)
{
now=++cnt;//新开一个版本
tr[now]=tr[ver];
if(l==r)
{
tr[now].val=w;
return ;
}
int m=l+r>>1;
if(m>=pos)
{
modify(l,m,tr[ver].l,tr[now].l,pos,w);
}
else
{
modify(m+1,r,tr[ver].r,tr[now].r,pos,w);
}
}
int query(int l,int r,int now,int pos)
{
if(l==r)
{
return tr[now].val;
}
int m=l+r>>1;
if(m>=pos)
{
return query(l,m,tr[now].l,pos);
}
else
return query(m+1,r,tr[now].r,pos);
}
int findx(int ver,int x)
{
int fa=query(1,n,root[ver],x);
if(fa!=x)
{
fa=findx(ver,fa);
}
return fa;
}
void mere(int ver,int a,int b)
{
a=findx(ver-1,a);
b=findx(ver-1,b);
if(a==b)
{
root[ver]=root[ver-1];
ran[ver]=ran[ver-1];
}
else
{
int da=query(1,n,ran[ver-1],a);
int db=query(1,n,ran[ver-1],b);
if(da<db)
{
modify(1,n,root[ver-1],root[ver],a,b);
ran[ver]=ran[ver-1];
}
else if(db<da)
{
modify(1,n,root[ver-1],root[ver],b,a);
ran[ver]=ran[ver-1];
}
else
{
modify(1,n,root[ver-1],root[ver],b,a);
modify(1,n,ran[ver-1],ran[ver],a,da+1);
}
}
}
int main()
{
int q;
scanf("%d %d",&n,&q);
build(1,n,root[0]);
for(int ver=1;ver<=q;ver++)
{
int op,a,b,x;
scanf("%d",&op);
switch(op)
{
case 1:
scanf("%d %d",&a,&b);
mere(ver,a,b);
break;
case 2:
scanf("%d",&x);
root[ver]=root[x];
ran[ver]=ran[x];
break;
case 3:
scanf("%d %d",&a,&b);
root[ver]=root[ver-1];
ran[ver]=ran[ver-1];
int fa=findx(ver,a);
int fb=findx(ver,b);
if(fa==fb)
printf("1\n");
else
printf("0\n");
break;
}
}
}