bzoj 4573

LCT神题...

首先暴力的做法就是每次在一个区间上link,然后暴力查询,时间复杂度$O(爆炸)$

但是我们可以发现的是,每棵树之间互不影响!

因此我们可以考虑离线之后分别统计每棵树

但是这样做其实并没有优化时空啊!

但是等等,我们在考虑一下:我们的操作都是区间操作,也就是说我们可以用一个类似差分的思想,一棵树一棵树地处理,处理到区间端点的时候修改一些东西就行了!

那么更换生长节点怎么办?

我们建立一个虚点表示生长节点,然后把新产生的点都连到这个点上就行

于是思路就顺理成章了:

对于新建节点的操作,我们新生成一个点权为1的节点,记录下这个节点生效的区间,然后把他挂到现在的生长节点上

对于更换生长节点的操作,我们新建一个点权为0的节点,将读入的区间与这个点生效的区间取交集作为有效的区间,在这个区间左端点把他挂在要求的实际节点上,右端点挂回虚节点链上

对于查询操作,直接用LCA查询即可,求LCA的方法见代码中access函数

然后离线所有操作,先按树的标号排序(注意新建节点的操作都认为是第一棵树上的,这样后面才能产生足够的节点去移动),先进行修改再进行查询(可以在排序时通过正负把查询排到后面)

这样问题就解决了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct Ques
{
    int pos,typ;
    int x,y;
    Ques (){}
    Ques (int a,int b,int c,int d):pos(a),typ(b),x(c),y(d){}
    friend bool operator < (Ques a,Ques b)
    {
        return a.pos==b.pos?a.typ<b.typ:a.pos<b.pos;
    }
}q[400005];
int ch[400005][2];
int siz[400005],f[400005],val[400005];
int ret[400005];
int fl[400005],fr[400005];
int lq[400005],rq[400005];
int n,m,Q;
int tot=0,cnt=0,ttop=0;
void update(int x)
{
    siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+val[x];
}
bool be_root(int x)
{
    if(ch[f[x]][0]==x||ch[f[x]][1]==x)return 0;
    return 1;
}
void rotate(int x)
{
    int y=f[x],z=f[y],k=(ch[y][1]==x);
    if(!be_root(y))ch[z][ch[z][1]==y]=x;
    f[x]=z;
    ch[y][k]=ch[x][!k],f[ch[x][!k]]=y;
    ch[x][!k]=y,f[y]=x;
    update(y),update(x);
}
void splay(int x)
{
    while(!be_root(x))
    {
        int y=f[x],z=f[y];
        if(!be_root(y))
        {
            if((ch[z][1]==y)^(ch[y][1]==x))rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    //update(x);
}
int access(int x)
{
    int y=0;
    while(x)
    {
        splay(x);
        ch[x][1]=y;
        update(x);
        y=x,x=f[x];
    }
    return y;
}
void link(int x,int y)
{
    splay(x),f[x]=y;
}
void cut(int x)
{
    access(x),splay(x),f[ch[x][0]]=0,ch[x][0]=0,update(x);
}
void new_node(int x)
{
    tot++,val[tot]=siz[tot]=x;
}
int query(int x,int y)
{
    int ret=0,fa;
    access(x),splay(x),ret+=siz[x];
    fa=access(y),splay(y),ret+=siz[y];
    access(fa),splay(fa),ret-=2*siz[fa];
    return ret;
}
inline int read()
{
    int f=1,x=0;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;
}
int main()
{
    n=read(),m=read();
    new_node(1),new_node(0);
    cnt=1,lq[1]=fr[1]=1,rq[1]=n;
    int las=2;link(2,1);
    for(int i=1;i<=m;i++)
    {
        int t=read();
        if(!t)
        {
            int l=read(),r=read();
            new_node(1);
            lq[++cnt]=l,rq[cnt]=r,fr[cnt]=tot;
            q[++ttop]=Ques(1,i-m,tot,las);
        }else if(t==1)
        {
            int l=read(),r=read(),x=read();
            l=max(l,lq[x]),r=min(r,rq[x]);
            if(l<=r)
            {
                new_node(0),link(tot,las);
                q[++ttop]=Ques(l,i-m,tot,fr[x]);
                q[++ttop]=Ques(r+1,i-m,tot,las);
                las=tot;
            }
        }else
        {
            int x=read(),st=read(),ed=read();
            q[++ttop]=Ques(x,++Q,fr[st],fr[ed]);
        }
    }
    sort(q+1,q+ttop+1);
    int last=1;
    for(int i=1;i<=n;i++)
    {
        while(q[last].pos==i&&last<=ttop)
        {
            if(q[last].typ<=0)cut(q[last].x),link(q[last].x,q[last].y);
            else ret[q[last].typ]=query(q[last].x,q[last].y);
            last++;
        }
    }
    for(int i=1;i<=Q;i++)printf("%d\n",ret[i]);
    return 0;
}

 

posted @ 2019-07-12 10:54  lleozhang  Views(58)  Comments(0Edit  收藏
levels of contents