[ZJOI2016]大森林

题目

洛谷
BZOJ
离线神仙题

做法

\((0)\)区间生长\((1)\)区间换点\((2)\)单点查询

考虑仅一次区间换点\((l,r,x)\),区间生长:
就相当于换点后的生长的点在l时接在\(x\)上,到\(r+1\)时再接回去

有多次换点也同理,对每个换点新增一个虚点,建立一条虚链,每次生长则\(link\)到最近的前一个虚点

然后离线升序排树处理

My complete code

#include<bits/stdc++.h>
using namespace std;
typedef int LL;
inline LL Read(){
    LL x(0),f(1); char c=getchar();
    while(c<'0' || c>'9'){ if(c=='-')c=-1; c=getchar(); }
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*f;
}
const LL maxn=1e6;
LL n,m,nod,tot,now,num;
LL son[maxn][2],fa[maxn],size[maxn],w[maxn],r[maxn],ans[maxn],sta[maxn],pos[maxn],gl[maxn],gr[maxn];
inline bool Nroot(LL x){ return (son[fa[x]][0]==x || son[fa[x]][1]==x); }
inline void Update(LL x){ size[x]=size[son[x][0]]+size[son[x][1]]+w[x]; }
inline void Rotate(LL x){
    LL y(fa[x]),z(fa[y]),lz(son[y][1]==x);
    if(Nroot(y)) son[z][son[z][1]==y]=x; fa[x]=z;
    son[y][lz]=son[x][lz^1];
    if(son[y][lz]) fa[son[y][lz]]=y;
    son[x][lz^1]=y, fa[y]=x;
    Update(y), Update(x);
}
inline void Splay(LL x){
    while(Nroot(x)){
        LL y=fa[x];
        if(Nroot(y)){
            LL z(fa[y]);
            ((son[y][1]==x)^(son[z][1]==y))?Rotate(y):Rotate(x);
        }Rotate(x);
    }
}
inline LL Access(LL x){
    LL lst(x);
    for(LL y=0;x;y=x,x=fa[x])
        lst=x, Splay(x), son[x][1]=y, Update(x);
    return lst;
}
inline void Link(LL x,LL y){
    Splay(x), fa[x]=y;
}
inline void Cut(LL x){
    Access(x), Splay(x);
    son[x][0]=fa[son[x][0]]=0;
    Update(x);
}
inline LL Lca(LL x,LL y){
    Access(x); return Access(y);
}

struct node{
    LL tree,id,x,y;
}a[maxn];
inline bool cmp(node x,node y){
    if(!(x.tree ^ y.tree)) return x.id<y.id;
    return x.tree<y.tree;
}
int main(){
    n=Read(), m=Read();
    nod=w[1]=size[1]=pos[1]=gl[1]=1,gr[1]=n;
    Link(now=tot=2,1);
    for(LL i=1;i<=m;++i){
        LL opt(Read()),l(Read()),r(Read());
        if(opt==0){
            Link(pos[++nod]=++tot,now);
            w[tot]=size[tot]=1;
            gl[nod]=l, gr[nod]=r;
        }else if(opt==1){
            LL x; cin>>x;
            l=max(l,gl[x]), r=min(r,gr[x]);
            if(l>r) continue;
            Link(++tot,now);
            a[++num]=(node){l,i-m,tot,pos[x]};
            a[++num]=(node){r+1,i-m,tot,now};
            now=tot;
        }else{
            LL x(l),u(r),v(Read());
            a[++num]=(node){x,++ans[0],pos[u],pos[v]};
        }
    }
    sort(a+1,a+1+num,cmp);
    for(LL i=1;i<=num;++i){
        if(a[i].id>0){
            LL x(a[i].x),y(a[i].y),lca(Lca(x,y));
            Access(x), Splay(x); ans[a[i].id]+=size[x];
            Access(y), Splay(y); ans[a[i].id]+=size[y];
            Access(lca), Splay(lca); ans[a[i].id]-=2*size[lca];
        }else
            Cut(a[i].x),Link(a[i].x,a[i].y);
    }
    for(LL i=1;i<=ans[0];++i) printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/y2823774827y/p/10413059.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值