P7492 序列
思路:
区间或、求区间最大连续字段和。
求区间最大连续字段和就是一个板子,用经典做法线段树维护一个 分别表示从左开始的最大子段和,右边开始的,区间的和,区间的答案。
因为一个数的二进制位只有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;
} [kuangbin带我飞]专题七 线段树 文章被收录于专栏
线段树入门到进阶 树不能只记得它的样子,要清楚它的性质,比如相邻点的关系

查看20道真题和解析