题面

区间反/翻转二进制,单点查询

解题思路

线段树,只不过要维护两个标记

分析

· 操作 $1$,对每位取反,直接异或上 (1<<k)+1 即可

· 操作 $2$,翻转二进制比较复杂,貌似也没有直接翻转的函数,就只能手写

再深究发现,其实两个操作是互不影响的,因此只需维护两个 $tag$ 即可,再看题目中的 $k$ 的限制,一系列操作的 $k$ 值相同,因此 $tag$ 维护的信息应为 $bool$ 类型,表示上述操作是否做了(因为同一个操作做两次就是无效的

又有 $t$ 小于 $5$,所以在 $k$ 值改变,也就是做下一组询问的时候,可以直接暴力把先前的标记全部下传

关于查询,其实只要把 对查询的点有影响的标记 下传即可,都不用维护区间

warning

貌似没有什么特别坑的地方,复制粘贴的时候记得改函数名就好了(尴尬

Code

#include<bits/stdc++.h>
#define rgt register
#define rint rgt int
#define LL long long
#define rll rgt LL
#define inf 0x7f7f7f7f
#define N 50003
using namespace std;
template<class K>inline bool cmax(K&a,const K&b){return (a<b)?a=b,1:0;}
template<class K>inline bool cmin(K&a,const K&b){return (a>b)?a=b,1:0;}
inline int read() {
    rint s=0;
    rgt char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) s=(s<<1)+(s<<3)+c-'0',c=getchar();
    return s;
}
int n,m,T,K,b[N],L,R,pos,y;
bool tag[N<<2],rev[N<<2];
inline void push(rint k,rint l,rint r) {//下传标记
    if(l==r) {//如果已经到了某个位置,直接修改
        if(tag[k]) b[l]^=y;//反转直接异或(1<<k)-1
        if(rev[k]) {//翻转要自己写
            rint i,c=b[l]&y,s=0;b[l]-=c;
            for(i=0;i<K;++i) {
                if(c&(1<<i)) s+=1<<(K-i-1);
            }b[l]+=s;
        }
    }
    else {//否则下传标记
        rint ls=k<<1;
        if(tag[k]) tag[ls]^=1,tag[ls|1]^=1;
        if(rev[k]) rev[ls]^=1,rev[ls|1]^=1;
    }
    tag[k]=rev[k]=0;
}
inline void Modify(rint k,rint l,rint r) {//打反转标记
    push(k,l,r);if(r<L||R<l) return;
    if(L<=l&&r<=R) {tag[k]^=1;return push(k,l,r);}
    rint m=(l+r)>>1,ls=k<<1;
    Modify(ls,l,m),Modify(ls|1,m+1,r);
}
inline void Flip(rint k,rint l,rint r) {//打翻转标记
    push(k,l,r);if(r<L||R<l) return;
    if(L<=l&&r<=R) {rev[k]^=1;return push(k,l,r);}
    rint m=(l+r)>>1,ls=k<<1;
    Flip(ls,l,m),Flip(ls|1,m+1,r);
}
inline void Query(rint k,rint l,rint r) {//查询 即将有影响的标记下传
    push(k,l,r);if(l==r) return;
    rint m=(l+r)>>1,ls=k<<1;
    (pos<=m)?Query(ls,l,m):Query(ls|1,m+1,r);
}
inline void Update(rint k,rint l,rint r) {//暴力下传所有标记
    push(k,l,r);if(l==r) return;
    rint m=(l+r)>>1,ls=k<<1;
    Update(ls,l,m),Update(ls|1,m+1,r);
}
int main() {
    rint i,op;
    n=read(),T=read();
    for(i=1;i<=n;i++) b[i]=read();
    while(T--) {
        m=read(),K=read(),y=(1<<K)-1;//取反异或用
        while(m--) {
            op=read();
            if(op==3) pos=read(),Query(1,1,n),printf("%d\n",b[pos]);//下传标记后直接输出当前值
            else {
                L=read(),R=read();
                if(op==1) Modify(1,1,n);
                else Flip(1,1,n);
            }
        }Update(1,1,n);//暴力下传标记
    }
    return 0;
}

devil.