洛谷3348 BZOJ4573 ZJOI2016 大森林 LCT 構造

題目鏈接

題意:
給你一個nn個點的森林,起初每棵樹都只有一個點,這個點是生長節點,長出的兒子會接在生長節點上。有mm次操作。要支持以下操作:第一個操作是讓一個區間的樹的生長節點全長出一個兒子,兒子的編號是這個位置的點現在的數量。第二個操作是讓一個區間的生長節點都變成某一個點。第三個操作是詢問第xx棵樹上點uu到點uu的距離。n<=1e5,m<=2e5n<=1e5,m<=2e5.

題解:
一道很神仙的題,我也不知道我懂了沒有,我更不知道我能不能講明白。

這個題的思想是,我們每次操作一個區間的樹,這樣會有一些區間的樹形態相同,所以我們考慮能不能不每一棵樹都建出來,用一個東西來維護許多LCT。我也不知道該怎麼說了,也不知道是怎麼想到的,反正是個很神仙的想法。

我們把詢問離線,然後對換生長節點和詢問操作按照操作位置排序,位置相同的先處理換生長節點。我們把一個換生長節點的操作拆成兩個,一個是在ll處出現這個操作,一個是在r+1r+1處這個影響消失。我們考慮建LCT,然後LCT上分兩類點,一類點是有權值的一類點是沒有權值的。沒有權值的點表示的是換生長節點的操作,有權值的點表示的是區間生長出一個葉子的操作。我們一開始建一個根節點,表示一開始所有的樹一開始都有一個點,這個根節點的權值是11。然後把所有的表示11操作的點連成一串,連的方法是,我們連向上一個出現的11操作。然後我們對於每一個00操作,我們也向上一個出現的11操作連邊。

我們排序之後按照位置順序開始做。先處理修改生長節點操作。如果是一個ll操作,我們就把它連到修改後的那個節點,如果是一個rr操作,那麼就改回到原來鏈接的那個它前面的一個11操作節點。對於詢問,我們就直接用兩個點到根的深度之和減去兩倍LCA的深度即可。用LCT求LCA的方法是我們access其中的一個點,然後再access另一個點,在第二個點access的過程中,最後一次虛實邊切換的時候的那個點就是LCA。

然後這個題由於最好保持樹的父子關係不變,所以沒有makeroot,而且也沒有子樹信息詢問,可以不用makeroot。

下面附上幾張網上用來解釋這個樣例構建LCT的過程的圖片。

五個操作排序後這樣子的:
0 1 5
0 1 4
2 1 1 3
1 2 4 2
2 2 1 3

初始形態

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述
講解差不多就到這裏。複雜度是O(nlogn)O(nlogn)

代碼:

#include <bits/stdc++.h>
using namespace std;

int n,m,f[600010],c[600010][2],s[600010],val[600010],rt,cnt;
int l[600010],r[600010],id[600010],num,shu,qwq,ans[600010];
struct node
{
    int opt,pos,x,y;
}a[300010];
inline int read()
{
    int x=0;
    char s=getchar();
    while(s>'9'||s<'0')
    s=getchar();
    while(s>='0'&&s<='9')
    {
        x=x*10+s-'0';
        s=getchar();
    }
    return x;
}
inline int cmp(node x,node y)
{
    if(x.pos==y.pos)
    return x.opt<y.opt;
    else
    return x.pos<y.pos;
}
inline int nroot(int x)
{
    return c[f[x]][0]==x||c[f[x]][1]==x;
}
inline void pushup(int x)
{
    s[x]=val[x]+s[c[x][0]]+s[c[x][1]];
}
inline void rotate(int x)
{
    int y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
    if(nroot(y))
    c[z][c[z][1]==y]=x;
    c[x][!k]=y;
    c[y][k]=w;
    if(w)
    f[w]=y;
    f[y]=x;
    f[x]=z;
    pushup(y);
    pushup(x);
}
inline void splay(int x)
{
    while(nroot(x))
    {
        int y=f[x],z=f[y];
        if(nroot(y))
        {
            if(c[y][1]==x ^ c[z][1]==y)
            rotate(x);
            else
            rotate(y);
        }
        rotate(x);
    }
    pushup(x);
}
inline int access(int x)
{
    int y=0;
    while(x)
    {
        splay(x);
        c[x][1]=y;
		if(y)
		f[y]=x;
        pushup(x);
        y=x;
        x=f[x];
    }
    return y;
}
inline void link(int x,int y)
{
    access(x);
    splay(x);
    f[x]=y;
}
inline void cut(int x)
{
    access(x);
    splay(x);
    f[c[x][0]]=0;
    c[x][0]=0;
    pushup(x);
}
int main()
{
    n=read();
    m=read();
    l[1]=1;
    r[1]=n;
    s[1]=1;
    val[1]=1;
    id[1]=1;
    rt=2;
    link(2,1);
    cnt=2;
    num=1;
    for(int i=1;i<=m;++i)
    {
        int opt=read();
        if(opt==0)
        {
            id[++num]=++cnt;
            link(cnt,rt);
            int x=read(),y=read();
            l[num]=x;
            r[num]=y;
            val[cnt]=1;
            s[cnt]=1;
        }
        else if(opt==1)
        {
            int x=read(),y=read(),z=read();
            x=max(x,l[z]);
            y=min(y,r[z]);
            if(x>y)
            continue;
            ++cnt;
            link(cnt,rt);
            a[++shu].opt=-1;
            a[shu].pos=x;
            a[shu].x=cnt;
            a[shu].y=id[z];
            a[++shu].opt=-1;
            a[shu].pos=y+1;
            a[shu].x=cnt;
            a[shu].y=rt;
            rt=cnt;
        }
        else
        {
            int x=read(),y=read(),z=read();
            a[++shu].pos=x;
            a[shu].x=id[y];
            a[shu].y=id[z];
            a[shu].opt=++qwq;
        }
    }
    sort(a+1,a+shu+1,cmp);
    for(int i=1;i<=shu;++i)
    {
        if(a[i].opt>0)
        {
            int res=0;
            access(a[i].x);
            splay(a[i].x);
            res+=s[a[i].x];
            int ji=access(a[i].y);
            splay(a[i].y);
            res+=s[a[i].y];
            access(ji);
            splay(ji);
            res-=2*s[ji];
            ans[a[i].opt]=res;
        }
        else
        {
            cut(a[i].x);
            link(a[i].x,a[i].y);
        }
    }
    for(int i=1;i<=qwq;++i)
    printf("%d\n",ans[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章