【BZOJ】3159决战-LCT

这篇博客介绍了如何利用双层LCT(Link-Cut Tree)解决BZOJ 3159问题。具体方法是建立一棵形态树并维护一棵权值树,区间翻转仅作用于权值树,其他操作同时更新两棵树。在形态树上的节点关系变化通过在权值树中寻找第k大节点来实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解

此题的一种经典做法:套两颗LCT,一颗维护权值,一颗维护形态。
形态树里的每一个节点都有一个对应的权值树里的点。
对于值的区间翻转,只翻转权值树。其他操作则两棵树一起操作。
在形态树中操作时,对于节点之间关系的修改,对应权值中的点可以通过bst找k大完成。

代码

#include<cstdio>
 #include<algorithm>
 #include<cstring> 
 #include<cctype>
 #include<cstdlib>
 using namespace std;
 typedef long long ll;
 const int N=5e4+10;
 const ll inf=1LL<<60;
 int n,m,R;
 char s[12];

 inline int rd()
 {
    char ch=getchar();int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
 }

 struct Ta{
    struct Node{
        Node *ch[2],*f;int sz,rv;
        ll ad,v,mn,mx,sum;
        void reverse(){
            rv^=1;swap(ch[0],ch[1]);
        }
        void update(){
            sz=ch[0]->sz+1+ch[1]->sz;
            sum=ch[0]->sum+v+ch[1]->sum;
            mn=min(min(ch[0]->mn,ch[1]->mn),v);
            mx=max(max(ch[0]->mx,ch[1]->mx),v); 
        } 
        void change(int w){
            ad+=w;v+=w;mn+=w;mx+=w;sum+=1ll*sz*w; 
        }
        void pushdown(Node *null){
            if(rv){
                if(ch[0]!=null) ch[0]->reverse();
                if(ch[1]!=null) ch[1]->reverse();
                rv=0; 
            }
            if(ad){
                if(ch[0]!=null) ch[0]->change(ad);
                if(ch[1]!=null) ch[1]->change(ad);
                ad=0; 
            }
        }
    }*null,t[N];
    Ta(){null=t;null->f=null->ch[0]=null->ch[1]=null;null->ad=null->sz=null->sum=0;null->mn=inf;null->mx=-inf;}
    void rotate(Node *x){
        if(x==null || x->f==null) return;Node *y=x->f;Node *z=y->f;
        int s= y->ch[1]==x,gs= z->ch[1]==y;x->f=z; 
        if(z!=null) z->ch[gs]=x; 
        y->ch[s]=x->ch[s^1];x->ch[s^1]->f=y;x->ch[s^1]=y;y->f=x;y->update();x->update();
    }
    Node *sta[N];
    void splay(Node *x){
       Node *y=x;int top=0;
       while(y!=null) {sta[++top]=y;y=y->f;}
       for(int i=top;i;i--) sta[i]->pushdown(null);
       while(x->f!=null){
          if(x->f->f==null) rotate(x);
          else {
            (x->f->f->ch[1]==x->f)^(x->f->ch[1]==x) ?rotate(x):rotate(x->f);rotate(x);
          }
       } 
    } 
    Node* kth(Node *x,int k){
       if(x->sz<k) return null;
       Node *y=x;
       while(k){
          y->pushdown(null);
          if(y->ch[0]->sz+1==k) break;
          y->ch[0]->sz>=k? y=y->ch[0]:(k-=(y->ch[0]->sz+1),y=y->ch[1]);
       }
       splay(y);return y;
    }
 }Val;

 struct Tb{
    struct Node{
        Node *ch[2],*p,*f;Ta:: Node *val;
        int rv,sz;
        void reverse(){
            rv^=1;swap(ch[0],ch[1]);
        }
        void update(){
            sz=ch[0]->sz+1+ch[1]->sz;
        }
        void pushdown(Node *null){
            if(rv){
                if(ch[0]!=null) ch[0]->reverse();
                if(ch[1]!=null) ch[1]->reverse();
                rv=0;
            }
        }
    }*null,t[N];
    Tb(){null=t;null->ch[0]=null->ch[1]=null->p=null->f=null;null->sz=0;}
    void rotate(Node *x){
        if(x==null || x->f==null) return;Node *y=x->f;Node *z=y->f;
        int s= y->ch[1]==x,gs= z->ch[1]==y;x->f=z; 
        if(z!=null) z->ch[gs]=x; 
        y->ch[s]=x->ch[s^1];x->ch[s^1]->f=y;x->ch[s^1]=y;y->f=x;y->update();x->update();
        swap(x->p,y->p);swap(x->val,y->val);//
    }
    Node *sta[N];
    void splay(Node *x){
       Node *y=x;int top=0;
       while(y!=null) {sta[++top]=y;y=y->f;}
       for(int i=top;i;i--) sta[i]->pushdown(null);
       while(x->f!=null){
          if(x->f->f==null) rotate(x);
          else {
            (x->f->f->ch[1]==x->f)^(x->f->ch[1]==x) ?rotate(x):rotate(x->f);rotate(x);
          }
       } 
    } 
    Node *access(Node *x){
        Node *y=null;int k;
        Ta:: Node *q,*e;
        while(x!=null){
            splay(x);
           if(x->ch[1]!=null){
              k=x->ch[0]->sz+2;
              q=Val.kth(x->val,k);
              Val.splay(q);
              e=q->ch[0];
              x->ch[1]->val=q;x->ch[1]->f=null;x->ch[1]->p=x;
              q->ch[0]->f=Val.null;q->ch[0]=Val.null;q->update();
              Val.splay(x->val=e);
           }
           if(y!=null){
              k=x->val->sz;
              q=Val.kth(x->val,k);
              Val.splay(q);
              q->ch[1]=y->val;q->ch[1]->f=q;q->update();
              Val.splay(x->val);
           }
           x->ch[1]=y;y->p=null;y->f=x;x->update();
           y=x;x=x->p;
        } 
        return y;
    }
    void mkrt(Node *x){
        Node *y=access(x);
        y->reverse();y->val->reverse();
    }
    void lk(Node *x,Node *y){
        mkrt(x);splay(x);x->p=y;access(x);
    }
    void rev(Node *x,Node *y){
        mkrt(x);
        access(y)->val->reverse();
    }
    void Add(Node *x,Node *y,int w){
        mkrt(x);
        access(y)->val->change(w);
    }
    ll Qsum(Node *x,Node *y){
        mkrt(x);
        return access(y)->val->sum;
    }
    ll Qmax(Node *x,Node *y){
        mkrt(x);
        return access(y)->val->mx;
    }
    ll Qmin(Node *x,Node *y){
        mkrt(x);
        return access(y)->val->mn;
    }
 }Lct;
 Ta :: Node *B[N];
 Tb :: Node *A[N];

 int main(){
    int i,j,x,y,z;
    n=rd();m=rd();R=rd();
    for(i=1;i<=n;++i) B[i]=Val.t+i;
    for(i=1;i<=n;++i){
        A[i]=Lct.t+i;A[i]->f=A[i]->p=A[i]->ch[0]=A[i]->ch[1]=Lct.null;
        A[i]->val=Val.t+i;A[i]->sz=1;A[i]->val->sz=1;
        A[i]->val->f=A[i]->val->ch[0]=A[i]->val->ch[1]=Val.null;
    }
    for(i=1;i<n;++i){x=rd();y=rd();Lct.lk(A[x],A[y]);}
    while(m--){
        scanf("%s",s);x=rd();y=rd();
        if(s[0]=='I' && s[2]=='c'){z=rd();Lct.Add(A[x],A[y],z);} 
        else if(s[0]=='S') printf("%lld\n",Lct.Qsum(A[x],A[y]));
        else if(s[0]=='M' && s[1]=='a') printf("%lld\n",Lct.Qmax(A[x],A[y]));
        else if(s[0]=='M' && s[1]=='i') printf("%lld\n",Lct.Qmin(A[x],A[y]));
        else Lct.rev(A[x],A[y]); 
    }
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值