题解 P5607 【[Ynoi2013]D1T1】
诗乃
2019-11-03 20:48:22
这个题太神啦QAQ
前置芝士:线段树,线性基。
求最大异或值,不难想到线性基。由于要求维护区间信息,采用线段树维护区间线性基。
但是区间修改很棘手(想了我两个小时QAQ我好菜菜)。考虑差分,将区间修改转化为两个单点修改。接下来考虑如何在差分后的序列上求答案。
记$\oplus$为异或操作,序列$b$满足$b_i=a_i \oplus a_{i-1}$,则有:
$$a_x=b_1 \oplus b_2 \oplus b_3 \oplus ....\oplus b_x$$
不难发现,对于$a$序列的区间$[l,r]$,其中任意一种数字选选取方案都可以用$a_l,b_{l+1},b_{l+2},b_{l+3}...b_{r}$中选取若干数表示出来,即**序列$a_l,a_{l+1},...a_r$的线性基与$a_l,b_{l+1},b_{l+2},...b_{r}$相同。**
查询时,求出$b_{l+1},b_{l+2},b_{l+3}...b_{r}$的线性基和$a_l$,将$a_l$插入线性基,贪心统计答案即可。
线段树上合并两个线性基复杂度$O(log^2V)$,因此总复杂度为$O(nlognlog^2V)$。
```cpp
#include <bits/stdc++.h>
#define L(u) (u<<1)
#define R(u) (u<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int MAXN = 50050;
void read(int &x) {
char ch; while(ch = getchar(), ch < '!'); x = ch - 48;
while(ch = getchar(), ch > '!') x = (x << 3) + (x << 1) + ch - 48;
}
struct XOR {
int d[30], s;
void init() {memset(d, 0, sizeof d);}
void insert(int x) {
for(int i = 29; i >= 0; --i)
if((x >> i) & 1) {
if(d[i]) x ^= d[i];
else {d[i] = x; break;}
}
}
} t[MAXN << 2]; int a[MAXN], n, m;
XOR merge(XOR a, XOR b) {
XOR res; res.init();
for(int i = 29; i >= 0; --i) if(a.d[i]) res.d[i] = a.d[i]; else res.d[i] = b.d[i];
for(int i = 29; i >= 0; --i) if(a.d[i] && b.d[i]) res.insert(b.d[i]);
res.s = a.s ^ b.s; return res;
}
void P(int u) {t[u] = merge(t[L(u)], t[R(u)]);}
void build(int u, int l, int r) {
if(l == r) {t[u].insert(t[u].s = a[l]); return;}
build(L(u), l, mid); build(R(u), mid+1, r); P(u);
}
void modify(int u, int l, int r, int p, int x) {
if(l == r) {t[u].init(), t[u].insert(t[u].s ^= x); return; }
p <= mid ? modify(L(u), l, mid, p, x) : modify(R(u), mid+1, r, p, x); P(u);
}
int finds(int u, int l, int r, int p) {
return l == r ? t[u].s : (p <= mid ? finds(L(u), l, mid, p) : finds(R(u), mid+1, r, p) ^ t[L(u)].s);
}
XOR findi(int u, int l, int r, int tl, int tr) {
if(tl <= l && r <= tr) return t[u];
if(tl > mid) return findi(R(u), mid+1, r, tl, tr);
if(tr <= mid) return findi(L(u), l, mid, tl, tr);
return merge(findi(L(u), l, mid, tl, tr), findi(R(u), mid+1, r, tl, tr));
}
int main() {
read(n); read(m); for(int i = 1; i <= n; ++i) read(a[i]);
for(int i = n-1; i; --i) a[i+1] ^= a[i]; build(1, 1, n);
for(int opt, l, r, x; m--; ) {
read(opt); read(l); read(r); read(x);
if(opt == 1) {
modify(1, 1, n, l, x);
if(r < n) modify(1, 1, n, r+1, x);
} else {
int tmp = finds(1, 1, n, l);
if(l == r) printf("%d\n", max(x, x^tmp));
else {
XOR t = findi(1, 1, n, l+1, r); t.insert(tmp);
for(int i = 29; i >= 0; --i) x = max(x, x ^ t.d[i]);
printf("%d\n", x);
}
}
}
}
```