雅礼培训 Problem A 【线段树】

题意

维护一段区间,支持求区间最大值,区间且,区间或
\(n,q<=2*10^5\)

题解

我们用线段树维护区间最大值

对于and和or运算,
and实质就是强行把一些位改为0
or实质就是强行把一些位改为1

那么由线段树区间标记的思想,如果某个操作对整个区间的影响是相同的,并且能很快维护出当前节点信息,我们就可以通过打标记进行优化
显然,当需要操作的那些位在整个区间都是相同的,我们就可以打上一个标记

对于统计区间相同位置,有一个小技巧:
维护区间or和区间and
and中为1的位区间都为1
or中为0的位区间都为0
二者整合起来就可以得到区间相同的位置集合

复杂度

如果区间总是不相同,复杂度会不会变差呢?
很显然,每次操作只能使所有位置相同的越来越多,而原来不同的位置经操作后一定变为相同
所以复杂度依旧是\(O(nlogn)\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k; k = ed[k].nxt)
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
const int maxn = 200005,maxm = 100005,maxv = (1 << 20) - 1,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}
    return out * flag;
}
int mx[4 * maxn],And[4 * maxn],Or[4 * maxn];
int vand[4 * maxn],vor[4 * maxn];
int n,A[maxn],Q;
void work_and(int u,int v){
    mx[u] &= v; And[u] &= v; Or[u] &= v;
    vand[u] &= v; vor[u] &= v;
}
void work_or(int u,int v){
    mx[u] |= v; And[u] |= v; Or[u] |= v;
    vand[u] |= v; vor[u] |= v;
}
void upd(int u){
    vand[u] = vand[ls] & vand[rs];
    vor[u] = vor[ls] | vor[rs];
    mx[u] = max(mx[ls],mx[rs]);
}
void pd(int u){
    if (And[u] != maxv){
        work_and(ls,And[u]);
        work_and(rs,And[u]);
        And[u] = maxv;
    }
    if (Or[u]){
        work_or(ls,Or[u]);
        work_or(rs,Or[u]);
        Or[u] = 0;
    }
}
void build(int u,int l,int r){
    And[u] = maxv;
    Or[u] = 0;
    if (l == r){
        mx[u] = vand[u] = vor[u] = A[l];
        return;
    }
    int mid = l + r >> 1;
    build(ls,l,mid);
    build(rs,mid + 1,r);
    upd(u);
}
void modify(int u,int l,int r,int L,int R,int v,int opt){
    if (l >= L && r <= R){
        if (opt == 1){
            if (((v ^ maxv) & ((vor[u] ^ maxv) | vand[u])) == (v ^ maxv)){
                work_and(u,v);
                return;
            }
        }
        else {
            if ((v & ((vor[u] ^ maxv) | vand[u])) == v){
                work_or(u,v);
                return;
            }
        }
    }
    pd(u);
    int mid = l + r >> 1;
    if (mid >= L) modify(ls,l,mid,L,R,v,opt);
    if (mid < R) modify(rs,mid + 1,r,L,R,v,opt);
    upd(u);
}
int query(int u,int l,int r,int L,int R){
    if (l >= L && r <= R) return mx[u];
    pd(u);
    int mid = l + r >> 1;
    if (mid >= R) return query(ls,l,mid,L,R);
    else if (mid < L) return query(rs,mid + 1,r,L,R);
    else return max(query(ls,l,mid,L,R),query(rs,mid + 1,r,L,R));
}
int main(){
    n = read(); Q = read();
    for (int i = 1; i <= n; i++) A[i] = read();
    build(1,1,n);
    int opt,l,r;
    while (Q--){
        opt = read();
        l = read();
        r = read();
        if (opt < 3) modify(1,1,n,l,r,read(),opt);
        else printf("%d\n",query(1,1,n,l,r));
    }
    return 0;
}

转载于:https://www.cnblogs.com/Mychael/p/8696359.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值