图片说明

思路:
区间或、求区间最大连续字段和。
求区间最大连续字段和就是一个板子,用经典做法线段树维护一个 分别表示从左开始的最大子段和,右边开始的,区间的和,区间的答案。
因为一个数的二进制位只有30位,而或操作只有将至少一个0变成1才对某个值有影响,所以有效的操作最多只会影响30n次改变,每次区间修改用单点修改然后再剪枝就行了。

MyCode:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7,mod=1e9+7;
typedef long long int ll;
int a[maxn<<2];
struct node{
    ll s,ls,rs,ans;
    node operator+(const node& a)const{
        return {s+a.s,max(ls,s+a.ls),max(a.rs,rs+a.s),max(ans,max(a.ans,rs+a.ls))};
    }
}t[maxn<<2];
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void build(int l,int r,int rt) {
    if(l==r) {
        cin>>a[rt];
        return t[rt]={a[rt],max(0,a[rt]),max(0,a[rt]),max(0,a[rt])},void();
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    t[rt]=t[rt<<1]+t[rt<<1|1];
    a[rt]=a[rt<<1]&a[rt<<1|1];
}
void update(int l,int r,int rt,int x,int y,int k) {
    if((a[rt]|k)==a[rt]) return;
    if(l==r) return t[rt]={a[rt]|=k,max(0,a[rt]),max(0,a[rt]),max(0,a[rt])},void();
    int mid=(l+r)>>1;
    if(x<=mid) update(lson,x,y,k);
    if(y>mid) update(rson,x,y,k);
    t[rt]=t[rt<<1]+t[rt<<1|1];
    a[rt]=a[rt<<1]&a[rt<<1|1];
}
node query(int l,int r,int rt,int x,int y) {
    if(l>=x&&r<=y) return t[rt];
    int mid=(l+r)>>1;
    if(y<=mid) return query(lson,x,y);
    if(x>mid) return query(rson,x,y);
    return query(lson,x,y)+query(rson,x,y);
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n,Q,op,L,R,k;
    cin>>n>>Q;
    build(1,n,1);
    while(Q--) {
        cin>>op>>L>>R;
        if(op&1) cout<<query(1,n,1,L,R).ans<<'\n';
        else {
            cin>>k;
            update(1,n,1,L,R,k);
        }
    }
    return 0;
}