【bzoj4753】【uoj#195】【LOJ2092】【ZJOI2016】大森林

显然0操作的时候可以当做所有树都加了这么一个点(但是后面挂到这个点的1操作的区间要对这个区间取min),这样0和2操作都跟时间没有关系了。
可以对每个1操作建一个虚点,点权为0,然后从它到下一个1操作间的所有0操作都可以连到这个虚点。虚点先默认连向上一次1操作,一边从左到右扫一边把1操作连到对应的点或者把它连回上一个1操作,这样就可以用LCT维护出所有位置的树。
但是这样查询的时候是会出锅的。查两个点距离的时候,如果我们直接split,可能会把一个虚点当做lca,实际上还要走到上面第一个实点。
其实有根树的LCT上是可以求lca的,access的时候返回最后转到根的那个点,求的时候access(x),然后access(y)返回的点就是lca(因为lca肯定是最后一个被access的点)。
我代码写得真是太shit了。。。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mxn=262144;
int rd(){
    int x=0;
    char c=getchar();
    for (;c<48||c>57;c=getchar());
    for (;c>47&&c<58;x=x*10+c-48,c=getchar());
    return x;
}
struct nd{
    int sum;
    bool cnt,tag;
    nd *ls,*rs,*fa;
}pool[mxn],*tpp=pool,*id[mxn],*stk[mxn];
bool noroot(nd *i){
    return i->fa&&(i->fa->ls==i||i->fa->rs==i);
}
void pushup(nd *i){
    i->sum=i->cnt;
    if (i->ls) i->sum+=i->ls->sum;
    if (i->rs) i->sum+=i->rs->sum;
}
void reverse(nd *i){
    if (i) swap(i->ls,i->rs),i->tag^=1;
}
void pushdown(nd *i){
    if (i->tag) i->tag=0,reverse(i->ls),reverse(i->rs);
}
void rotate(nd *i){
    nd *f=i->fa,*s;
    if (noroot(f))
        if (f->fa->ls==f) f->fa->ls=i;
        else f->fa->rs=i;
    else;
    i->fa=f->fa,f->fa=i;
    if (f->ls==i) f->ls=s=i->rs,i->rs=f;
    else f->rs=s=i->ls,i->ls=f;
    if (s) s->fa=f;
    pushup(f),pushup(i);
}
void splay(nd *x,int tp=1){
    stk[1]=x;
    for (nd *y=x;noroot(y);stk[++tp]=y=y->fa);
    for (;tp;pushdown(stk[tp--]));
    for (nd *y;noroot(x);rotate(x))
        if (noroot(y=x->fa)) rotate((y->ls==x)^(y->fa->ls==y)?x:y);
}
nd *access(nd *x,nd *y=0){
    for (;x;x=(y=x)->fa)
        splay(x),x->rs=y,pushup(x);
    return y;
}
void link(nd *x,nd *y){
    splay(x),x->fa=y;
}
void cut(nd *x,nd *y){
    access(y),splay(y),splay(x),x->fa=0;
}
int n,m,q,tot,k,lb[mxn],rb[mxn],cr[mxn],ty[mxn],ans[mxn];
struct node{
    int id,pos,x,y,z;
}a[mxn];
bool operator<(const node x,const node y){
    return x.pos<y.pos||(x.pos==y.pos&&x.id<y.id);
}
int main()
{
    m=rd(),q=rd();
    lb[1]=1,rb[1]=m,id[1]=++tpp;
    n=1,m=0;
    tpp->sum=tpp->cnt=1;
    for (int i=1;i<=q;++i){
        int typ=rd();
        if (!typ){
            id[++n]=++tpp,tpp->sum=tpp->cnt=1;
            lb[n]=rd(),rb[n]=rd();
        }
        else if (typ==1)
            a[++tot].x=rd(),a[tot].y=rd(),a[tot].pos=rd(),cr[++k]=n;
        else ++tot,a[tot]=(node){++m,rd(),rd(),rd(),0},ty[tot]=1;
    }
    cr[k+1]=n,k=0;
    q=tot;
    for (int i=2;i<=cr[1];++i) link(id[i],id[1]);
    for (int i=1,lst=1,cur=cr[1]+1;i<=tot;++i)
        if (!ty[i]){
            int l=max(a[i].x,lb[a[i].pos]),r=min(a[i].y,rb[a[i].pos]);
            node x=(node){(l>r)*(m+1),l,++n,lst,a[i].pos};
            a[i]=x;
            a[++q]=(node){(l>r)*(m+1),r+1,n,a[i].z,lst};
            id[n]=++tpp,link(id[n],id[lst]),lst=n;
            for (++k;cur<=cr[k+1];++cur) link(id[cur],id[n]);
        }
    sort(a+1,a+q+1);
    tot=0;
    for (int i=1;i<=q;++i)
        if (!a[i].id)
            cut(id[a[i].x],id[a[i].y]),link(id[a[i].x],id[a[i].z]);
        else if (a[i].id<=m){
            access(id[a[i].x]),splay(id[1]),ans[a[i].id]+=id[1]->sum;
            nd *z=access(id[a[i].y]);splay(id[1]),ans[a[i].id]+=id[1]->sum;
            access(z),splay(id[1]),ans[a[i].id]-=2*id[1]->sum;
            if (++tot==m) break;
        }
    for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/zzqtxdy/p/11488107.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值