Review-并查集(Bzoj3673)

本文介绍了一种数据结构——可持久化并查集,它通过加入时间维度实现了状态回溯的功能。适用于需要记录历史状态及快速查询的场景。文章提供了具体的实现代码示例。

3673: 可持久化并查集 by zky

Time Limit: 5 Sec   Memory Limit: 128 MB
Submit: 478   Solved: 196
[ Submit][ Status]

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 Input

5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2

Sample Output

1
0

1


题解:

可持续数据结构的思想:利用时间戳,加上时间一维,就比如在我们的三维世界加上第四维-—时间轴,这样就可以回到原来的状态。具体实现是用线段树表示每个点的父亲节点,当父亲节点修改时,在造出需要的修改的节点,因此空间消耗有点大。

代码:

program pro;
var
    ke,ls,rs:array[0..2000005]of longint;
    n,m,tot:longint;
    fa,deep,root:array[0..2000005]of longint;

procedure swap(var x,y:longint);
var
    k:longint;
begin
    k:=x; x:=y; y:=k;
end;

procedure make(var now:longint; l,r:longint);
var
    mid:longint;
begin
    if now=0 then
    begin
        inc(tot); now:=tot;
    end;
    if l=r then
    begin
        fa[now]:=l;
        exit;
    end;
    mid:=(l+r)shr 1;
    make(ls[now],l,mid);
    make(rs[now],mid+1,r);
end;

procedure modify(l,r,x:longint; var y:longint; pos,val:longint);
var
    mid:longint;
begin
    inc(tot); y:=tot;
    if l=r then
    begin
        fa[y]:=val; deep[y]:=deep[x];
        exit;
    end;
    ls[y]:=ls[x]; rs[y]:=rs[x];
    mid:=(l+r)shr 1;
    if pos<=mid then modify(l,mid,ls[x],ls[y],pos,val)
    else modify(mid+1,r,rs[x],rs[y],pos,val);
end;

function query(k,l,r,pos:longint):longint;
var
    mid:longint;
begin
    if l=r then exit(k);
    mid:=(l+r)shr 1;
    if pos<=mid then exit(query(ls[k],l,mid,pos))
    else exit(query(rs[k],mid+1,r,pos));
end;

procedure add(k,l,r,pos:longint);
var
    mid:longint;
begin
    if l=r then
    begin
        inc(deep[k]); exit;
    end;
    mid:=(l+r)shr 1;
    if pos<=mid then add(ls[k],l,mid,pos)
    else add(rs[k],mid+1,r,pos);
end;

function find(k,x:longint):longint;
var
    xx:longint;
begin
    xx:=query(k,1,n,x);
    if fa[xx]=x then exit(xx)
    else exit(find(k,fa[xx]));
end;

procedure main;
var
    i,j,re,a,b,aa,bb,tmp:longint;
begin
    readln(n,m);
    make(root[0],1,n);
    for i:=1 to m do
    begin
        read(re);
        case re of
        1:
        begin
            root[i]:=root[i-1];
            readln(a,b);
            aa:=find(root[i],a); bb:=find(root[i],b);
            if fa[aa]=fa[bb] then continue;
            if deep[aa]>deep[bb] then swap(aa,bb);
            modify(1,n,root[i-1],root[i],fa[aa],fa[bb]);
            if (deep[aa]=deep[bb]) then add(root[i],1,n,fa[bb]);
        end;
        2:
        begin
            readln(tmp);
            root[i]:=root[tmp];
        end;
        3:
        begin
            root[i]:=root[i-1];
            readln(a,b);
            aa:=find(root[i],a); bb:=find(root[i],b);
            if fa[aa]=fa[bb] then writeln(1) else writeln(0);
        end;
        end;
    end;
end;

begin
assign(input,'3673.in'); reset(input);
    main;
close(input);
end.



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值