思路:
区间或、求区间最大连续字段和。
求区间最大连续字段和就是一个板子,用经典做法线段树维护一个 分别表示从左开始的最大子段和,右边开始的,区间的和,区间的答案。
因为一个数的二进制位只有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; }