3674: 可持久化并查集加强版 主席树+并查集按秩合并

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

题解:

很久之前就做了另一个版本……当时乱写,结果交过来就TLE了……今天填了一下这个坑,发现要用并查集的按秩合并,秩其实就是树的高度,并的话就把秩小的并到秩大的上,这样可以保证树的高度最小,也就保证了查询时的时间复杂度。(因为不能压缩路径)。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=200010;
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;
}
struct tree{int lc,rc,fa,dep;}tr[maxn*30];
int len=0,root[maxn];
int n,m;
void build(int l,int r)
{
    int t=++len;
    if(l==r) tr[t].fa=l,tr[t].dep=1;
    if(l<r)
    {
        int mid=l+r>>1;
        tr[t].lc=len+1;build(l,mid);
        tr[t].rc=len+1;build(mid+1,r);
    }
}
int get(int now,int l,int r,int x)
{
    if(l==r) return now;
    int mid=l+r>>1;
    if(x<=mid) return get(tr[now].lc,l,mid,x);
    else return get(tr[now].rc,mid+1,r,x);
}
int findfa(int rt,int x)
{
    int t=get(rt,0,n,x);
    if(tr[t].fa==x) return t;
    else return findfa(rt,tr[t].fa);
}
void update(int &now,int last,int l,int r,int x,int c)
{
    int t=++len;now=t;
    tr[t]=tr[last];
    if(l==r){tr[t].fa=c;tr[t].dep++;return;}
    int mid=l+r>>1;
    if(x<=mid)update(tr[t].lc,tr[last].lc,l,mid,x,c);
    else update(tr[t].rc,tr[last].rc,mid+1,r,x,c);
}
void add_dep(int &now,int last,int l,int r,int x)
{
    int t=++len;now=t;
    tr[t]=tr[last];
    if(l==r){tr[t].dep++;return;}
    int mid=l+r>>1;
    if(x<=mid)add_dep(tr[t].lc,tr[last].lc,l,mid,x);
    else add_dep(tr[t].rc,tr[last].rc,mid+1,r,x);
}
int main()
{
    int ans=0;
    n=read();m=read();
    build(0,n);root[0]=1;
    for(int i=1;i<=m;++i)
    {
        root[i]=root[i-1];
        int o=read(),x=read()^ans,y;
        if(o==1)
        {
            y=read()^ans;
            int fx=findfa(root[i-1],x),fy=findfa(root[i-1],y);
            if(tr[fx].fa!=tr[fy].fa)
            {
                if(tr[fx].dep>tr[fy].dep)swap(fx,fy);
                update(root[i],root[i-1],0,n,tr[fx].fa,tr[fy].fa);
                if(tr[fx].dep==tr[fy].dep)add_dep(root[i],root[i],0,n,tr[fy].fa);
            }
        }
        else if(o==2) root[i]=root[x];
        else
        {
            y=read()^ans;
            int fx=findfa(root[i-1],x),fy=findfa(root[i-1],y);
            printf("%d\n",ans=(tr[fx].fa==tr[fy].fa));
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值