P3402 【模板】可持久化并查集

其实看看代码自己就可以懂。

注意: 1.并查集不压缩路径,压缩了就回不到压缩之前的状态了。

2.并查集合并时,小的往大的合并,启发式合并。

3.对于第i步(不管什么操作)操作都要把root[i] = roo[i-1]//我在合并时,如果两个祖先一样就直接continue了,没把root[i]赋值为root[i-1],一直没看出来,拖了一个多月才过。。。。

#include<iostream>
#include<cstdio>
using namespace std;
int n, m, tot, fa[4000005], siz[4000005], root[200005], ls[4000005], rs[4000005];
int build(int o,int l,int r)
{
    int nd = ++tot;
    if(l == r)
    {
        fa[nd] = l; siz[nd] = 1;   return nd;
    }
    int mid = (l + r) >> 1;
    ls[nd] = build(o*2, l, mid);
    rs[nd] = build(o*2+1, mid+1, r);
    return nd;
}
int qurey_fa(int o, int l, int r, int pos)
{
    if(l == r)  return fa[o];
    int mid = (l + r) >> 1;
    if(mid >= pos) return qurey_fa(ls[o], l, mid, pos);
    else return qurey_fa(rs[o], mid+1, r, pos);
}
int find(int x,int bb)
{
    int ff = qurey_fa(root[bb],1,n,x);
    if(ff == x) return x;
    else return find(ff,bb);
}
int qurey_size(int o, int l, int r, int pos)
{
    if(l == r)   return siz[o];
    int mid = (l + r) >>1;
    if(mid >= pos) return qurey_size(ls[o], l, mid, pos);
    else return qurey_size(rs[o], mid+1, r, pos);
}
int modify_fa(int o, int l, int r, int pos, int val)
{
    int nd = ++tot;
    rs[nd] = rs[o];   ls[nd] = ls[o];  fa[nd] = fa[o];  siz[nd] = siz[o];
    if(l == r)
    {
        fa[nd] = val;    return nd;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) ls[nd] = modify_fa(ls[o], l, mid, pos, val);
    else rs[nd] = modify_fa(rs[o], mid+1, r, pos, val);
   return nd;
}
int  modify_size(int o, int l, int r, int pos, int val)
{
    int  nd = ++tot;
    rs[nd] = rs[o];  ls[nd] = ls[o];  fa[nd] = fa[o];  siz[nd] = siz[o];
    if(l == r)
    {
        siz[nd] += val;  return nd;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid)ls[nd] =  modify_size(ls[o], l, mid, pos, val);
    else rs[nd] =  modify_size(rs[o], mid+1, r, pos, val);
    return nd;
}
int main()
{
    cin >> n >> m;
    root[0] = build(1,1,n);//建树,把fa[i] 赋值为i
    for(int i = 1; i <= m; i++)
    {
     int a,b,c;   scanf("%d", &a);
      if(a == 1)
      {
      scanf("%d%d", &b, &c);
      int r1 = find(b,i-1);  int r2 = find(c,i-1);//寻找i-1步时c的祖先是谁	
      if(r1 == r2){root[i] = root[i-1]; continue;}//如果祖先一样,continue;
      int s1 = qurey_size(root[i-1],1,n,r1);
      int s2 = qurey_size(root[i-1],1,n,r2);//询问大小
      if(s1 < s2){ swap(s1,s2);   swap(r1,r2);}//启发式合并。小合大,我规定r1大 
      root[i] =  modify_fa(root[i-1],1,n,r2,r1);//把r2的爸爸改为r1,只改r2.
      root[i] =  modify_size(root[i],1,n,r1,s2);//把r1并查集的大小加上r2并查集的大小
      }
      else if(a == 2) {scanf("%d",&b);  root[i] = root[b];}//回到操作b。
      else 
      {
      	scanf("%d%d", &b, &c);
      	root[i] = root[i-1];
      	int r1 = find(b,i-1);  int r2 = find(c,i-1);
        if(r1 == r2) cout << "1" << endl;
        else cout << "0"<<endl;	
      } 
    }
    return 0;
} 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值