Luogu3348 [ZJOI2016]大森林

Luogu3348 [ZJOI2016]大森林

\(LCT\)

这道题初看十分恐怖,需要维护区间加叶子节点,区间修改生长节点,每棵树中加入叶子节点的位置可能不同,很难对于每棵树直接维护。

虽然每棵树都会产生大量信息,但是我们如果冷静分析一下,就会发现有许多信息是重复的。

我们先使问题简单化。

注意到\(0\)操作中添加的点只能作用在一段区间上,我们可以去除这个限制,让新的节点加到每一棵树上,因为在一次操作中,产生的节点编号是固定的,而且不会在其他操作中生成,同时不可能询问一棵树中不存在的节点,所以我们就可以让节点在每一棵树上生成。

\(1\)操作中提到,如果一棵树上没有节点\(x\),则不修改,不过我们注意到,存在节点\(x\)的一定是一段区间的树(代码中注意节点\(1\)的存在区间为\([1,n]\)),因此我们将修改区间与存在\(x\)节点的区间取一个交集就可以获得真正修改的区间。

对于操作\(2\),我们观察到每棵树只会添加节点,而没有随意断边连边,这就保证了我们的树在\(t\)时刻的那一部分形态不会在以后被改变,因此我们可以先建完树,然后一次性处理完所有询问,这就启发我们离线下来解决。

假设我们处理完了第\(x\)棵树,然后去处理第\(x+1\)棵树,一个很自然的想法就是利用第\(x\)棵树的信息。

我们可以去寻找第\(x\)棵树和第\(x+1\)棵树的相同点,由于我们去掉了\(0\)操作的区间限制,如果不存在\(1\)操作,第\(x,x+1\)棵树的形态必定相同。

加入了\(1\)操作之后,我们依然可以发现,在两次间隔的\(1\)操作之间,第\(x,x+1\)棵树新添加的节点形态仍然相同,只是生长节点不同罢了,因此我们可以考虑如何把这些节点搬运过去。

我们显然不能一个一个搬运,因此我们可以建立一个虚点来统辖这些节点,这样我们搬运时只需要一步\(cut\),一步\(link\)就可以了。

我们可以每到达一个\(1\)操作,就建立一个虚点,让这些虚点统辖从这次操作到下一次\(1\)操作之间产生的节点。

然后我们记录一下那些位置需要搬运,对于操作\(1\)的真正修改区间\([l,r]\),我们在从\(l-1\)转移到\(l\)时需要搬运一次,从\(r\)转移到\(r+1\)时需要搬运一次。

因此我们总搬运次数不超过\(2M\),可以保证时间复杂度。

我们有了大致的框架,先不修改生长节点,按题目要求建出一棵带虚点的树(可以看成第\(0\)棵树),然后通过搬运依次转移到第\(1\sim n\)棵树,当我们处理完第\(i\)棵树时,解决所有关于第\(i\)棵树的回答。

由于只需要搬运子树,我们的\(LCT\)不需要用到\(makeroot\)操作。

如何处理回答呢?我们先给出结论,假设\(h_i\)表示节点\(i\)到根节点\(1\)实节点数量,那么一对点\(x,y\)的答案就是\(h_x+h_y-2h_{lca(x,y)}\)

想必有人会对上面的式子产生疑问,答案难道不是\(x\)\(y\)路径上实节点数量\(-1\)吗,正常的计算链上节点数量是\(h_x+h_y-h_{lca(x,y)}-h_{fa_{lca(x,y)}}\),因此答案难道不是\(h_x+h_y-h_{lca(x,y)}-h_{fa_{lca(x,y)}}-1\)吗?

解释一下这个问题,首先,如果\(lca(x,y)\)为实节点,那么上式显然成立,因为\(h_{lca(x,y)}=h_{fa_{lca(x,y)}}+1\)。当\(lca(x,y)\)为虚节点时,根据我们建树的过程,虽然\((x,y)\)在树上的\(lca\)是虚点,但是实际上在原树中它们的\(lca\)是虚点的父亲\(u\),因为虚点只是代理管辖一下\(u\)的子孙节点而已,在真正的树中,\(u\)\(x,y\)所在的链上,而我们在利用\(LCT\)统计实节点数时忽略了这个节点,因此:

\[ ans=(h_x+h_y-h_{lca(x,y)}-h_{fa_{lca(x,y)}}-1)+1\\ =h_x+h_y-h_{lca(x,y)}-h_{fa_{lca(x,y)}}\\ =h_x+h_y-2h_{lca(x,y)} \]

那么这道题就完美解决了。

\(Pascal \quad Code:\)

type node=record
    fa,num,v:longint;
	ch:array[0..1] of longint;
end;
type qry=record
    o,x,u,v,idt:longint;
end;
var
    n,m,i,opt,rl,cnt,gr,l,r,x,u,v,tot,qc:longint;
    zl,zr,re:array[0..200005] of longint;
	a:array[0..200005] of node;
    g:array[0..400005] of qry;
    ans:array[0..200005] of longint;
function cmn(x,y:longint):longint;
begin
    if x<y then
        exit(x) else
        exit(y);
end;
function cmx(x,y:longint):longint;
begin
    if x>y then
        exit(x) else
        exit(y);
end;
function ls(x:longint):longint;
begin
    exit(a[x].ch[0]);
end;
function rs(x:longint):longint;
begin
    exit(a[x].ch[1]);
end;
function fa(x:longint):longint;
begin
    exit(a[x].fa);
end;
function num(x:longint):longint;
begin
    exit(a[x].num);
end;
function va(x:longint):longint;
begin
    exit(a[x].v);
end;
function isrt(x:longint):boolean;
begin
    exit((ls(fa(x))<>x) and (rs(fa(x))<>x));
end;
function id(x:longint):longint;
begin
    if ls(fa(x))=x then
        exit(0);
    exit(1);
end;
procedure update(x:longint);
begin
    a[x].num:=num(ls(x))+num(rs(x))+va(x);
end;
procedure connect(x,f,son:longint);
begin
    a[x].fa:=f;
    a[f].ch[son]:=x;
end;
procedure rot(x:longint);
var
    y,r,yson,rson:longint;
begin
    y:=fa(x);
    r:=fa(y);
	yson:=id(x);
	rson:=id(y);
    if isrt(y) then
        a[x].fa:=r else
        connect(x,r,rson);
    connect(a[x].ch[yson xor 1],y,yson);
    connect(y,x,yson xor 1);
    update(y);
    update(x);
end;
procedure splay(x:longint);
var
    y:longint;
begin
    while not isrt(x) do
    begin
        y:=fa(x);
        if isrt(y) then
            rot(x) else
        if id(x)=id(y) then
        begin
            rot(x);
            rot(x);
        end else
        begin
            rot(y);
            rot(x);
        end;
    end;
end;
procedure access(x:longint);
var
    y:longint;
begin
    y:=0;
    while x<>0 do
    begin
        splay(x);
        a[x].ch[1]:=y;
        update(x);
        y:=x;
        x:=fa(x);
    end;
end;
function access_lca(x,y:longint):longint;
var
    z,ans:longint;
begin
    z:=0;
    ans:=0;
    while x<>0 do
    begin
        splay(x);
        a[x].ch[1]:=z;
        update(x);
        z:=x;
        x:=fa(x);
    end;
    inc(ans,num(z));
    z:=0;
    while y<>0 do
    begin
        splay(y);
        a[y].ch[1]:=z;
        update(y);
        z:=y;
        y:=fa(y);
    end;
    y:=z;
    inc(ans,num(z));
    ans:=ans-(num(z)-num(rs(z)))*2;
    exit(ans);
end;
procedure link(x,y:longint);
begin
    a[x].fa:=y;
end;
procedure cut(x:longint);
begin
    access(x);
    splay(x);
    a[ls(x)].fa:=0;
    a[x].ch[0]:=0;
    update(x);
end;
procedure ins(o,x,u,v,idt:longint);
begin
    inc(tot);
    g[tot].o:=o;
    g[tot].x:=x;
    g[tot].u:=u;
    g[tot].v:=v;
    g[tot].idt:=idt;
end;
function cmp(x,y:qry):boolean;
begin
    if x.x<>y.x then
        exit(x.x<y.x);
    exit(x.o<y.o);
end;
procedure sort(l,r:longint);
var
    i,j,k:longint;
    t:qry;
begin
    i:=l;
    j:=r;
    k:=(l+r) div 2;
    t:=g[k];
    g[k]:=g[i];
    while i<j do
    begin
        while (i<j) and cmp(t,g[j]) do
            dec(j);
        if i<j then
        begin
            g[i]:=g[j];
            inc(i);
        end;
        while (i<j) and cmp(g[i],t) do
            inc(i);
        if i<j then
        begin
            g[j]:=g[i];
            dec(j);
        end;
    end;
    g[i]:=t;
    if i-1>l then
        sort(l,i-1);
    if i+1<r then
        sort(i+1,r);
end;
begin
    read(n);
	read(m);
    cnt:=2;
    gr:=2;
	rl:=1;
	qc:=0;
	re[1]:=1;
	a[1].v:=1;
	zl[1]:=1;
	zr[1]:=n;
	update(1);
    link(2,1);
    for i:=1 to m do
    begin
        read(opt);
        case opt of
            0:  begin
                    read(l);
                    read(r);
                    inc(rl);
                    inc(cnt);
                    a[cnt].v:=1;
                    update(cnt);
                    re[rl]:=cnt;
                    link(cnt,gr);
                    zl[rl]:=l;
                    zr[rl]:=r;
                end;
            1:  begin
                    read(l);
                    read(r);
                    read(x);
                    l:=cmx(l,zl[x]);
                    r:=cmn(r,zr[x]);
                    if l>r then
                        continue;
                    x:=re[x];
                    inc(cnt);
                    link(cnt,gr);
                    ins(0,l,cnt,x,0);
                    ins(0,r+1,cnt,gr,0);
                    gr:=cnt;
                end;
            2:  begin
                    read(x);
                    read(u);
                    read(v);
                    inc(qc);
                    ins(1,x,re[u],re[v],qc);
                end;
		end;
    end;
    sort(1,tot);
    for i:=1 to tot do
    begin
        if g[i].o=0 then
        begin
            cut(g[i].u);
            link(g[i].u,g[i].v);
        end else
			ans[g[i].idt]:=access_lca(g[i].u,g[i].v);
    end;
    for i:=1 to qc do
        writeln(ans[i]);
end.
posted @ 2021-02-22 21:41  GK0328  阅读(62)  评论(0编辑  收藏  举报