P6781 [Ynoi2008] rupq

Zaunese發表於2024-06-15

P6781 [Ynoi2008] rupq

線段樹上維護這種括號序列,如果資訊可差分是好做的,但現在只能合併。

先說如何合併資訊。

  • max 是簡單的。
  • 至於 nand,不需要考慮結合律,只要維護一個 bool[32][2] 表示當某一位的第一個運算元是 0/1 時,經過它們的傳遞、運算的結果是什麼。見於 P2114 [NOI2014] 起床困難綜合症

列舉演算法發現可以用兔隊線段樹(單側遞迴)來做。

考慮它做樓房重建(嚴格字首最大值)的演算法,calc 過程中,對於一棵子樹,求大於 \(x\) 的嚴格字首最大值個數:

  • 如果處於葉子,返回節點值是否大於 \(x\)
  • 如果此樹最大 <= \(x\),返回 0;
  • 如果左最大 <= \(x\),那麼只要右的答案,向右遞迴;
  • 否則需要合併右側傳上來的答案(而不是右節點存的答案)與左側遞迴答案(大於 \(x\) 的嚴格字首最大值個數)。

此處右側傳上來的答案是用 tr[x].ans-tr[x*2].ans 算的。這回不能差分,所以在 x 處把這個存下來。還要儲存左括號與右括號資訊。

考慮它做現問題的演算法,calc 過程中,對於一棵子樹,求前 \(x\) 個左括號的資訊:

  • 如果處於葉子或左括號剛好 \(x\) 個,返回左括號資訊;
  • 如果 左側左括號數 不多於 右側右括號數,那麼只要右的答案,向右遞迴;
  • 否則考慮左側剩餘的左括號數:
    • 如果不少於 \(x\) 就直接向左遞迴;
    • 否則需要合併左側傳上來的答案(而不是左節點存的答案)與右側遞迴答案。

右括號同理。


考慮交換區間。把線段樹改造成 WBLT。以程式碼 EDU。

點選檢視程式碼
//#define dxx
#ifdef dxx
#define dbg(...) fprintf(stderr,__VA_ARGS__)
#define dex(a) dbg(#a"=%lld onL%d infun %s\n",(ll)a,__LINE__,__FUNCTION__)
#include<cstdlib>
#define pause sys##tem("read -p \"panss2continue..\"")
#define _GLIBCXX_DEBUG
#endif

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

#define fi first
#define se second
#define mkp std::make_pair
using ll=long long;
using llu=unsigned long long;
using std::max;
using std::min;
template<class T> void cmax(T&a,T b){a=max(a,b);}
template<class T> void cmin(T&a,T b){a=min(a,b);}
template<class T> T sqr(T a){return a*a;}

const int NV=2e6;

int N,a[NV+5],b[NV+5];

struct INFO{
    unsigned mx,tdr[2],cnt;
    INFO(unsigned _mx=0,unsigned tab0=0,unsigned tab1=-1,unsigned _cnt=0){
        mx=_mx;
        cnt=_cnt;
        tdr[0]=tab0;
        tdr[1]=tab1;
    }
};
INFO operator+(INFO a,INFO b){
    return INFO(max(a.mx,b.mx),
        ~a.tdr[0]&b.tdr[0]|a.tdr[0]&b.tdr[1],
        ~a.tdr[1]&b.tdr[0]|a.tdr[1]&b.tdr[1],
        a.cnt+b.cnt
    );
}

namespace wblt{
    struct SEGN{
        INFO lw,rw,s;
        int l,r,siz;
    } tr[5000006];
    int trash[NV+5],cnt,rt;
    int create(){
        return *trash?trash[(*trash)--]:++cnt;
    }void del(int x){
        trash[++*trash]=x;
    }int create(int x,int y){
        int z=create();
        tr[z].l=tr[z].r=0;
        tr[z].siz=1;
        tr[z].lw=tr[z].rw=tr[z].s=INFO();
        (x?tr[z].rw:tr[z].lw)=INFO(y,-1,~y,1);
        return z;
    }INFO quer(int x,int K){
        if(tr[x].siz==1||tr[x].rw.cnt==K) return tr[x].rw;
        if(tr[tr[x].l].rw.cnt<=tr[tr[x].r].lw.cnt)
            return quer(tr[x].r,K);
        else{
            int t=tr[tr[x].l].rw.cnt-tr[tr[x].r].lw.cnt;
            if(K<=t) return quer(tr[x].l,K);
            else return tr[x].s+quer(tr[x].r,K-t);
        }
    }INFO quel(int x,int K){
        if(tr[x].siz==1||tr[x].lw.cnt==K) return tr[x].lw;
        if(tr[tr[x].l].rw.cnt>=tr[tr[x].r].lw.cnt)
            return quel(tr[x].l,K);
        else{
            int t=tr[tr[x].r].lw.cnt-tr[tr[x].l].rw.cnt;
            if(K<=t) return quel(tr[x].r,K);
            else return quel(tr[x].l,K-t)+tr[x].s;
        }
    }void up(int x){
        tr[x].siz=tr[tr[x].l].siz+tr[tr[x].r].siz;
        const int ls=tr[x].l,rs=tr[x].r,delta=(int)tr[ls].rw.cnt-tr[rs].lw.cnt;
        if(delta>0){
            auto t=quer(ls,delta);
            tr[x].s=t;
            tr[x].lw=tr[ls].lw;
            tr[x].rw=t+tr[rs].rw;
        }else if(delta<0){
            auto t=quel(rs,-delta);
            tr[x].s=t;
            tr[x].lw=tr[ls].lw+t;
            tr[x].rw=tr[rs].rw;
        }else{
            tr[x].lw=tr[ls].lw;
            tr[x].rw=tr[rs].rw;
            tr[x].s=INFO(0,0,-1,0);
        }
    }int merge(int x,int y){
        int z=create();
        tr[z].l=x;
        tr[z].r=y;
        up(z);
        return z;
    }int build(int l,int r){
        if(l==r) return create(a[l],b[l]);
        int mid=l+r>>1;
        return merge(build(l,mid),build(mid+1,r));
    }void doupd(int x,int z){
        if(tr[x].rw.cnt){
            tr[x].rw=INFO();
            tr[x].lw=INFO(z,-1,~z,1);
        }else{
            tr[x].lw=INFO();
            tr[x].rw=INFO(z,-1,~z,1);
        }
    }void upd(int x,int p,int z){
        if(tr[x].siz==1){
            doupd(x,z);
            return;
        }
        int mid=tr[tr[x].l].siz;
        if(p<=mid) upd(tr[x].l,p,z);
        else upd(tr[x].r,p-mid,z);
        up(x);
    }int merot(int x,int y){
        if(!x||!y) return x|y;
        if(tr[x].siz>tr[y].siz*4){
            int t=merge(tr[x].l,merot(tr[x].r,y));
            del(x);
            return t;
        }
        if(tr[y].siz>tr[x].siz*4){
            int t=merge(merot(x,tr[y].l),tr[y].r);
            del(y);
            return t;
        }
        return merge(x,y);
    }void split(int x,int K,int&a,int&b){
        if(!K){
            a=0;
            b=x;
            return;
        }
        if(tr[x].siz==1){
            a=x;
            b=0;
            return;
        }
        int mid=tr[tr[x].l].siz;
        if(K<=mid){
            split(tr[x].l,K,a,b);
            del(x);
            b=merot(b,tr[x].r);
        }else{
            split(tr[x].r,K-mid,a,b);
            del(x);
            a=merot(tr[x].l,a);
        }
    }int split(int x,int l,int r){
        if(l==1&&r==tr[x].siz) return x;
        int mid=tr[tr[x].l].siz;
        if(r<=mid) return split(tr[x].l,l,r);
        if(mid<l) return split(tr[x].r,l-mid,r-mid);
        return merge(split(tr[x].l,l,mid),split(tr[x].r,1,r-mid));
    }void swp(int l,int r){
        int x,y,z;
        split(rt,l-1,x,y);
        split(y,r-l+1,y,z);
        rt=merot(merot(x,z),y);
    }unsigned que(int l,int r){
        int tmpc=cnt,tmpt=*trash;
        int x=split(rt,l,r);
        cnt=tmpc;
        *trash=tmpt;
        auto ans=tr[x].lw+tr[x].rw;
        return ans.mx^ans.tdr[1];
    }
}

namespace xm{
    void _(){
        int Q;

        scanf("%d%d",&N,&Q);
        for(int i=1;i<=N;++i) scanf("%d%d",a+i,b+i);
        wblt::rt=wblt::build(1,N);
        while(Q--){
            int x,y;
            short op;
            scanf("%hd%d%d",&op,&x,&y);
            if(op==1) wblt::upd(wblt::rt,x,y);
            else if(op==2) printf("%u\n",wblt::que(x,y));
            else wblt::swp(x,y);
        }
    }
}

signed main(){
    xm::_();
    return 0;
}