题意:
给定一个长度为 n n n 的数组 a a a,有 q q q 次操作,① 1 , l , r , x 1, l, r, x 1,l,r,x,区间 & x \&~x & x;② 2 , l , r , x 2, l, r, x 2,l,r,x,区间 ∣ x \mid~x ∣ x;③ 3 , l , r 3, l, r 3,l,r,查询区间最大值。 ( n , q ≤ 2 × 1 0 5 , 0 ≤ a i ≤ 2 20 ) (n, q \leq 2×10^5, 0 \leq a_i \leq 2^{20}) (n,q≤2×105,0≤ai≤220)
链接:
https://vjudge.net/problem/HDU-5828
解题思路:
位运算考虑拆位用线段树维护,但需要维护的是最值,不像区间和可以分开维护。区间 & x \&~x & x,最值可以直接维护当且仅当区间所有数相同,更进一步考虑,将每个位分开更新,某个位可以直接更新当且仅当区间所有数该位相同。那么分每个位考虑操作, & x \&~x & x,当 x i = 0 x_i = 0 xi=0 有效,区间赋值 0 0 0; ∣ x \mid~x ∣ x,当 x i = 1 x_i = 1 xi=1 有效,区间赋值 1 1 1,区间直接更新当且仅当区间所有数该位相同,定义结点势能为 0 0 0 当且仅当满足该位相同,否则为 1 1 1。注意以下讨论仍只考虑单个二进制位,其他各位同理。初始总势能为 O ( n l o g n ) O(nlogn) O(nlogn),更新时先定位 O ( l o g n ) O(logn) O(logn) 个区间,若区间该位不全相同,即势能非 0 0 0,那么暴力向下访问,回溯时势能清 0 0 0,暴力访问的结点数与势能同阶。
综上,对每位单独考虑更新即可,但不必也不能分开用多棵线段树维护。总复杂度 O ( n l o g n l o g a + m l o g n ) O(nlognloga + mlogn) O(nlognloga+mlogn)。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 2e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int msk = (1 << 21) - 1;
int a[maxn];
int n, m;
struct SegTree{
int mx[maxn << 2], equ[maxn << 2], tad[maxn << 2], tor[maxn << 2];
void pushUp(int rt){
mx[rt] = max(mx[lson], mx[rson]);
equ[rt] = equ[lson] & equ[rson] & (mx[lson] ^ mx[rson] ^ msk);
}
void build(int l, int r, int rt){
tad[rt] = msk, tor[rt] = 0;
if(l == r){
mx[rt] = a[l], equ[rt] = msk;
return;
}
int mid = gmid;
build(l, mid, lson);
build(mid + 1, r, rson);
pushUp(rt);
}
void pushDown2(int rt, int son){
tad[son] &= tad[rt];
tor[son] &= tad[rt];
mx[son] &= tad[rt];
tor[son] |= tor[rt];
mx[son] |= tor[rt];
}
void pushDown(int rt){
pushDown2(rt, lson);
pushDown2(rt, rson);
tad[rt] = msk, tor[rt] = 0;
}
void updateAnd(int l, int r, int rt, int L, int R, int val){
if(l >= L && r <= R){
tad[0] = (val ^ msk) & equ[rt] ^ msk, tor[0] = 0;
pushDown2(0, rt);
val ^= tad[0] ^ msk;
if(val == msk) return;
}
int mid = gmid;
pushDown(rt);
if(L <= mid) updateAnd(l, mid, lson, L, R, val);
if(R > mid) updateAnd(mid + 1, r, rson, L, R, val);
pushUp(rt);
}
void updateOr(int l, int r, int rt, int L, int R, int val){
if(l >= L && r <= R){
tad[0] = msk, tor[0] = val & equ[rt];
pushDown2(0, rt);
val ^= tor[0];
if(!val) return;
}
int mid = gmid;
pushDown(rt);
if(L <= mid) updateOr(l, mid, lson, L, R, val);
if(R > mid) updateOr(mid + 1, r, rson, L, R, val);
pushUp(rt);
}
int query(int l, int r, int rt, int L, int R){
if(l >= L && r <= R) return mx[rt];
int mid = gmid, ret = 0;
pushDown(rt);
if(L <= mid) ret = max(ret, query(l, mid, lson, L, R));
if(R > mid) ret = max(ret, query(mid + 1, r, rson, L, R));
return ret;
}
} tr;
int main(){
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; ++i){
cin >> a[i];
}
tr.build(1, n, 1);
while(m--){
int opt, x, y, z; cin >> opt >> x >> y;
if(opt == 1){
cin >> z;
tr.updateAnd(1, n, 1, x, y, z);
}
else if(opt == 2){
cin >> z;
tr.updateOr(1, n, 1, x, y, z);
}
else{
int ret = tr.query(1, n, 1, x, y);
cout << ret << "\n";
}
}
return 0;;
}