题解 无力回天 NOI2017
询问最大 xor \operatorname{xor} xor 和,支持修改,想到线段树套线性基。
但是操作一又做不了:打不了 tag。为什么打不了 tag 呢?因为 tag 只能记录一个数,而线段树维护的是线性基而不是异或和,如果打标记的话要打上很多数。
所以只能单点修改。
考虑用线段树维护差分数组,然后操作二又怎么做呢?
设差分数组为 b b b,如果我们要查询 [ a l , a r ] [a_l,a_r] [al,ar] 的线性基(设其为线性基一),可以查询 a l , [ b l + 1 , b r ] a_l,[b_{l+1},b_r] al,[bl+1,br] 的线性基(设其为线性基二)。
接下来证明二线性基等价:
若在线性基二中选择 b k b_k bk,可在线性基一中选择 a k − 1 , a k a_{k-1},a_k ak−1,ak;若在线性基一种选择 a k a_k ak,如果 k = l k=l k=l,在线性基二中选择 a l a_l al,如果 k ≠ l k≠l k=l,可以选择 a l , [ b l + 1 , b k ] a_l,[b_{l+1},b_k] al,[bl+1,bk]。
感性理解一下。
所以我们使用线段树维护 b b b 数组。但是还有个 a l a_l al 怎么处理呢?想想我们会怎么操作 a a a 数组:区间修,单点查。所以用线段树和树状数组维护都可以。如果用树状数组维护则需维护差分数组。
注意:差分数组要从后往前算。
注意:线段树单点修改到叶子节点时,线性基要进行重构,此时可以顺便维护下差分数组,如要使第 k k k 位异或,令 a k = a k xor v a_k=a_k \operatorname{xor} v ak=akxorv,再将此数插入线性基。
所以现在有: a a a 数组维护差分,树状数组维护差分的前缀和即原数,线段树维护线性基。问题解决了!
//P5607
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
int n, m, a[N], bit[N];
struct node{
int p[32], l, r;
void ins(int k){
for(int i = 31; i >= 0; -- i){
if(!(k&(1<<i))) continue;
if(!p[i]){ p[i] = k; break; }
k ^= p[i];
}
}
void ins(node &k){
for(int i = 31; i >= 0; -- i) ins(k.p[i]);
}
int mx(int v){
for(int i = 31; i >= 0; -- i) v = max(v, v ^ p[i]);
return v;
}
} T[N<<2], res;
struct SegTree{
void buildtree(int p, int l, int r){
T[p].l = l, T[p].r = r;
if(l == r) T[p].ins(a[l]);
else{
int mid = l + r >> 1;
buildtree(p<<1, l, mid);
buildtree(p<<1|1, mid+1, r);
T[p].ins(T[p<<1]); T[p].ins(T[p<<1|1]);
}
}
void modify(int p, int x, int v){
if(T[p].l == T[p].r){
memset(T[p].p, 0, sizeof(T[p].p));
T[p].ins(a[T[p].l] ^ v);
a[T[p].l] ^= v;
} else {
int mid = T[p].l + T[p].r >> 1;
if(x <= mid) modify(p<<1, x, v);
else modify(p<<1|1, x, v);
memset(T[p].p, 0, sizeof(T[p].p));
T[p].ins(T[p<<1]); T[p].ins(T[p<<1|1]);
}
}
void query(int p, int l, int r){
if(l <= T[p].l && T[p].r <= r) res.ins(T[p]);
else {
int mid = T[p].l + T[p].r >> 1;
if(l <= mid) query(p<<1, l, r);
if(mid < r) query(p<<1|1, l, r);
}
}
} ST;
struct BIT{
int lb(int x){ return x & -x; }
void add(int p, int v){
while(p <= n) bit[p] ^= v, p += lb(p);
}
int pre(int r){
int ans = 0;
while(r) ans ^= bit[r], r -= lb(r);
return ans;
}
} BIT;
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]);
for(int i = n; i; -- i) a[i] ^= a[i-1];
for(int i = 1; i <= n; ++ i) BIT.add(i, a[i]);
ST.buildtree(1, 1, n);
for(int i = 1, op, l, r, v; i <= m; ++ i){
scanf("%d%d%d%d", &op, &l, &r, &v);
if(op == 1){
ST.modify(1, l, v), BIT.add(l, v);
if(r < n) ST.modify(1, r+1, v), BIT.add(r+1, v);
} else {
memset(res.p, 0, sizeof(res.p));
res.ins(BIT.pre(l)); ST.query(1, l+1, r);
printf("%d\n", res.mx(v));
}
}
return 0;
}
怎么说呢,至少也是一道 Ynoi,虽然思维难度可能比别的 Ynoi 简单一点,但是细节是一点也不少的。