题意

\(n\)个位置,每个位置一个栈,三种操作,询问区间栈顶的和,区间入栈某个数,单点出栈某个数。

分析

  • 用一个线段树来维护栈顶的和,区间(单点)更新和区间询问。
  • 用一个主席树来维护每个位置最新一次入栈的时间,即主席树存的是时间,然后取出的时间也能作为主席树的下标来访问对应时间的版本
  • 因此区间入栈的时候区间更新线段树和区间更新主席树。
  • 单点出栈时,先查询这个位置栈顶元素的入栈时间,然后再用这个时间的上一个版本的主席树查询栈顶下一个元素的入栈时间,根据入栈时间可以知道入栈的元素,然后由于栈顶已经出栈,所以新的栈顶就是该元素,单点更新线段树和主席树。

代码

#include <bits/stdc++.h>
#define ls i<<1
#define rs i<<1|1
#define mid (l+r)/2
using namespace std;
typedef long long ll;
const int N=8e5+50;
int n,m,ty,o,l1,r1,x[N];
struct ST{
    //lz:区间赋值标记 sum:区间和
    ll lz[N*4],sum[N*4];
    void pushup(int i){
        sum[i]=sum[ls]+sum[rs];
    }
    void pushdown(int i,int l,int r){
        if(lz[i]){
            lz[ls]=lz[i];
            lz[rs]=lz[i];
            sum[ls]=(mid-l+1)*lz[i];
            sum[rs]=(r-mid)*lz[i];
            lz[i]=0;
        }
    }
    void update(int i,int l,int r,int ql,int qr,int v){
        if(ql<=l && qr>=r){
            lz[i]=v;
            sum[i]=1ll*(r-l+1)*v;
            return;
        }
        pushdown(i,l,r);
        if(ql<=mid){
            update(ls,l,mid,ql,qr,v);
        }
        if(qr>mid){
            update(rs,mid+1,r,ql,qr,v);
        }
        pushup(i);
    }
    ll query(int i,int l,int r,int ql,int qr){
        if(ql<=l && qr>=r){
            return sum[i];
        }
        pushdown(i,l,r);
        ll ans=0;
        if(ql<=mid){
            ans+=query(ls,l,mid,ql,qr);
        }
        if(qr>mid){
            ans+=query(rs,mid+1,r,ql,qr);
        }
        return ans;
    }
}st;
int tr[N];
struct CT{
    int tot=0,tim[N*50],lr[N*50],rr[N*50];
    int update(int pre,int l,int r,int ql,int qr,int t){
        int rt=++tot;
        tim[rt]=t;
        lr[rt]=lr[pre];
        rr[rt]=rr[pre];
        if(ql<=l && qr>=r){
            lr[rt]=rr[rt]=(l==r?0:rt);
        }else{
            if(ql<=mid){
                lr[rt]=update(lr[pre],l,mid,ql,qr,t);
            }
            if(qr>mid){
                rr[rt]=update(rr[pre],mid+1,r,ql,qr,t);
            }
        }
        return rt;
    }
    int query(int i,int l,int r,int v){
        if(l==r && l==v){
            return tim[i];
        }
        if(v<=mid){
            return query(lr[i],l,mid,v);
        }else{
            return query(rr[i],mid+1,r,v);
        }
    }
}ct;
int dec(int l1,int lst){
    return (l1+lst*ty)%n+1;
}
int main(){
//    freopen("in.txt","r",stdin);
    scanf("%d%d%d",&n,&m,&ty);
    ll lst=0;
    for(int t=1;t<=m;t++){
        scanf("%d",&o);
        tr[t]=tr[t-1];
        if(o==1){
            scanf("%d%d",&l1,&r1);
            l1=dec(l1,lst);
            r1=dec(r1,lst);
            int l=min(l1,r1);
            int r=max(l1,r1);
            //直接查询线段树区间和
            lst=st.query(1,1,n,l,r);
            printf("%lld\n",lst);
        }else if(o==2){
            scanf("%d",&l1);
            int l=dec(l1,lst);
            //查询l这个位置最新插入的时间
            int tm=ct.query(tr[t],1,n,l);
            //查询再前一个插入时间,即将最新插入出栈
            int pt=ct.query(tr[tm-1],1,n,l);
            //线段树单点修改为上一个版本
            st.update(1,1,n,l,l,x[pt]);
            //主席树对当前最新版本单点修改l位置的插入时间
            tr[t]=ct.update(tr[t],1,n,l,l,pt);
        }else if(o==3){
            scanf("%d%d%d",&l1,&r1,&x[t]);
            l1=dec(l1,lst);
            r1=dec(r1,lst);
            int l=min(l1,r1);
            int r=max(l1,r1);
            //线段树区间更新
            st.update(1,1,n,l,r,x[t]);
            //前面已经有tr[t]=tr[t-1] 无论更不更新都复制一个新版本
            //主席树区间更新
            tr[t]=ct.update(tr[t],1,n,l,r,t);
        }
    }
    return 0;
}