[bzoj4573][UOJ#195][ZJOI2016]大森林

13 篇文章 0 订阅
12 篇文章 0 订阅

题目描述

小Y家里有一个大森林,里面有n棵树,编号从1到n。一开始这些树都只是树苗,只有一个节点,标号为1。这些树
都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。小Y掌握了一种魔法,能让第l棵树
到第r棵树的生长节点长出一个子节点。同时她还能修改第l棵树到第r棵树的生长节点。她告诉了你她使用魔法的
记录,你能不能管理她家的森林,并且回答她的询问呢?
第一行包含 2 个正整数 n,m,共有 n 棵树和 m 个操作。接下来 m 行,每行包含若干非负整数表示一个操作,操
作格式为:
0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 号操作叶子标号
加 1(例如,第一个 0 号操作产生的子节点标号为 2), l 到 r 之间的树长出的节点标号都相同。保证 1≤l≤
r≤n 。
1 l r x 表示将第 l 棵树到第 r 棵树的生长节点改到标号为 x 的节点。对于 i (l≤i≤r)这棵树,如果标号 x
的点不在其中,那么这个操作对该树不产生影响。保证 1≤l≤r≤n , x 不超过当前所有树中节点最大的标号。
2 x u v 询问第 x 棵树中节点 u 到节点 v 点的距离,也就是在第 x 棵树中从节点 u 和节点 v 的最短路上边的
数量。保证1≤x≤n,这棵树中节点 u 和节点 v 存在。N<=10^5,M<=2*10^5

splay维护括号序做法

我们可以想到离线去做
把0和1操作分别挂左右端点,进入时插入离开时删除。
从左到右,我们需要维护树的形态信息,同时需要支持插入、删除一个0或1操作。
容易想到用括号序把树表示出来。
插入0操作的话,这个操作对应点的父亲是在其时间之前最晚一个存在的1操作的对应点。
删除0操作也一样。
而插入1操作会使得一连串的0操作更换了自己的父亲。
为了能够准确分离出这段,我们还得规定同一个点的子树严格按时间顺序。
插入时在splay上二分一下。
询问的话,括号序是可以求两点的距离的。
然后因为1操作可能不合法,我们其实可以把1操作的对应区间根据其对应0操作的对应区间进行更改,就不用考虑1操作不合法了。
这样写起来比较难

LCT大法

我们对于每一个1操作建一个虚点。
为了区分真实存在的点与辅助用的虚点,我们引入点权,虚点为0实点为1。
每一个0操作对应点都挂在其前面时间最晚的1操作对应虚点下。
当一个1操作当前不生效时,其也挂在其前面时间最晚的1操作对应虚点下,但若其生效了,我们把它挂在其对应点下。
这样做显然保证了正确性。
那么随手打个LCT就能做了。

#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200000+10;
struct dong{
    int t,ca,x,y,z,ans;
    friend bool operator <(dong a,dong b){
        return a.t<b.t;
    }
} ask[maxn],zlt;
int h[maxn],go[maxn*2],next[maxn*2],dis[maxn*2],b[maxn],gs[maxn],lt[maxn],rt[maxn];
int h2[maxn],g2[maxn],n2[maxn];
int h3[maxn],g3[maxn],n3[maxn],now[maxn];
int father[maxn*2],tree[maxn*2][2],key[maxn*2],sum[maxn*2],pp[maxn*2],sta[maxn*2];
bool bz[maxn*2];
int i,j,k,l,r,t,x,y,n,m,tot,top,cnt,num,ans;
int read(){
    int x=0,f=1;
    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;
}
void add(int x,int y,int z){
    go[++cnt]=y;
    dis[cnt]=z;
    next[cnt]=h[x];
    h[x]=cnt;
}
int pd(int x){
    return tree[father[x]][1]==x;
}
void update(int x){
    sum[x]=sum[tree[x][0]]+sum[tree[x][1]]+key[x];
}
void rotate(int x){
    int y=father[x],z=pd(x);
    father[x]=father[y];
    if (father[y]) tree[father[y]][pd(y)]=x;
    tree[y][z]=tree[x][1-z];
    if (tree[x][1-z]) father[tree[x][1-z]]=y;
    tree[x][1-z]=y;
    father[y]=x;
    update(y);
    update(x);
    if (pp[y]) pp[x]=pp[y],pp[y]=0;
}
void clear(int x){
    if (bz[x]){
        if (tree[x][0]) bz[tree[x][0]]^=1;
        if (tree[x][1]) bz[tree[x][1]]^=1;
        swap(tree[x][0],tree[x][1]);
        bz[x]=0;
    }
}
void remove(int x,int y){
    top=0;
    while (x!=y){
        sta[++top]=x;
        x=father[x];
    }
    while (top){
        clear(sta[top]);
        top--;
    }
}
void splay(int x,int y){
    remove(x,y);
    while (father[x]!=y){
        if (father[father[x]]!=y)
            if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
        rotate(x);
    }
}
int access(int x){
    int y,z,p=x;
    splay(x,0);
    z=tree[x][1];
    tree[x][1]=0;
    if (z){
        father[z]=0;
        pp[z]=x;
    }
    update(x);
    while (pp[x]){
        p=y=pp[x];
        splay(y,0);
        z=tree[y][1];
        if (z){
            father[z]=0;
            pp[z]=y;
        }
        tree[y][1]=x;
        father[x]=y;
        pp[x]=0;
        update(y);
        splay(x,0);
    }
    return p;
}
void makeroot(int x){
    access(x);
    splay(x,0);
    bz[x]^=1;
}
void link(int x,int y){
    makeroot(y);
    splay(y,0);
    pp[y]=x;
}
void cut(int x){
    makeroot(1);
    access(x);
    splay(x,0);
    int y=tree[x][0];
    tree[x][0]=0;
    if (y){
        father[y]=0;
        pp[y]=0;
    }
    update(x);
}
void insert1(int x){
    int j=b[ask[x].y-1],k=b[ask[x].y];
    cut(k);
    link(k,ask[x].x);
}
void delete1(int x){
    int j=b[ask[x].y-1],k=b[ask[x].y];
    cut(k);
    link(k,j);
}
void insert0(int x){
    int j=gs[ask[x].x];
    key[ask[x].x]=1;
    update(ask[x].x);
    link(ask[x].x,b[j]);
}
void delete0(int x){
    int j=gs[ask[x].x];
    cut(ask[x].x);
}
void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        sta[++top]=x%10;
        x/=10;
    }
    while (top){
        putchar('0'+sta[top]);
        top--;
    }
    putchar('\n');
}
int main(){
    //freopen("ex_forest2.in","r",stdin);freopen("data.out","w",stdout);
    n=read();m=read();
    tot=1;top=1;
    lt[1]=1;rt[1]=n;
    fo(i,1,m){
        ask[i].t=i;
        ask[i].ca=read();
        if (ask[i].ca==0){
            l=read();r=read();
            add(l,i,1);add(r+1,i,-1);
            ask[i].x=++tot;
            lt[tot]=l;rt[tot]=r;
            gs[tot]=top;
        }
        else if (ask[i].ca==1){
            l=read();r=read();
            ask[i].x=read();
            l=max(l,lt[ask[i].x]);r=min(r,rt[ask[i].x]);
            if (l<=r){
                add(l,i,1);add(r+1,i,-1);
                ask[i].y=++top;
            }
        }
        else{
            ask[i].z=l=read();
            add(l,i,1);
            ask[i].x=read();ask[i].y=read();
        }
    }
    zlt.t=0;
    zlt.ca=1;
    zlt.x=1;
    zlt.y=1;
    ask[0]=zlt;
    fo(i,1,top) b[i]=++tot;
    key[1]=1;
    update(1);
    pp[b[1]]=1;
    fo(i,2,top) pp[b[i]]=b[i-1];
    cnt=0;
    fo(i,1,n){
        if (i==441){
            t=t;
        }
        t=h[i];
        while (t){
            if (ask[go[t]].ca==0){
                if (dis[t]==1) insert0(go[t]);else delete0(go[t]);
            }
            else if (ask[go[t]].ca==1){
                if (dis[t]==1) insert1(go[t]);else delete1(go[t]);
            }
            t=next[t];
        }
        t=h[i];
        while (t){
            if (ask[go[t]].ca==2){
                x=ask[go[t]].x;y=ask[go[t]].y;
                if (x==y){
                    ask[go[t]].ans=0;
                    t=next[t];
                    continue;
                }
                makeroot(1);
                access(x);
                splay(x,0);
                ans=sum[x];
                x=access(y);
                splay(y,0);
                ans+=sum[y];
                //x=find(x);
                access(x);
                splay(x,0);
                ans-=sum[x]*2;
                ask[go[t]].ans=ans;
            }
            t=next[t];
        }
    }
    /*cnt=0;top=0;
    fo(i,1,m)
        if (ask[i].ca==2) now[++top]=i;
    fo(i,1,top){
        if (ask[now[i-1]].ans==3&&ask[now[i]].ans==4&&ask[now[i+1]].ans==3){
            cnt++;
            if (cnt==1) break;
        }
    }
    t=t;*/
    fo(i,1,m)
        if (ask[i].ca==2) write(ask[i].ans);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值