题解 P5607 【[Ynoi2013]无力回天NOI2017】
刚学线性基……水一发吧(
首先这个题要求的是区间异或最大和,所以就可以看出来是个xxx树套线性基的做法。
又因为线性基不支持减,支持
但是这个区间异或的修改就很难受……又没法直接上标记……
考虑差分,设
然后我们再发现一个性质:
再去考虑线性基:若有
所以其实就证明了
所以现在就可以维护
那么这道题就解决了~
最终总时间复杂度 (话说lxl确实没几个3log的题呢),空间复杂度
还有一个注意点就是要用另外一个数据结构(我这里用的是树状数组)来维护
上代码~
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
using namespace std;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline int qread() {
register char c = getchar();
register int x = 0, f = 1;
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + c - 48;
c = getchar();
}
return x * f;
}
inline int Abs(const int& x) {return (x > 0 ? x : -x);}
inline int Max(const int& x, const int& y) {return (x > y ? x : y);}
inline int Min(const int& x, const int& y) {return (x < y ? x : y);}
int n, m, a[50005], c[50005];
//线性基
struct LineBase {
int a[35];
//往线性基里面插入一个值x
inline void Insert(int x) {
for (register int i = 29;~i;i--) {
if (x & (1 << i)) {
if (a[i]) x ^= a[i];
else {
a[i] = x;
return;
}
}
}
}
};
//两个线性基合并
inline LineBase Merge(LineBase x, LineBase y) {
for (register int i = 29;i >= 0;i--) {
if (y.a[i]) x.Insert(y.a[i]);
}
return x;
}
//维护线性基的线段树
struct Segtree {
LineBase nd[200005];
//建树
inline void Init(int p, int l, int r) {
for (register int i = l;i <= r;i++) nd[p].Insert(a[i]);
if (l == r) return;
register int mid = l + r >> 1;
Init(p << 1, l, mid);
Init(p << 1 | 1, mid + 1, r);
nd[p] = Merge(nd[p << 1], nd[p << 1 | 1]);
}
//单点修改
inline void Modify(int p, int pl, int pr, int idx, int v) {
if (pl == pr) {
memset(nd[p].a, 0, sizeof(nd[p].a));//因为叶子的线性基里面只有1个数所以可以直接重构
nd[p].Insert(a[pl] ^= v);
return;
}
register int mid = pl + pr >> 1;
if (idx <= mid) Modify(p << 1, pl, mid, idx, v);
else Modify(p << 1 | 1, mid + 1, pr, idx, v);
nd[p] = Merge(nd[p << 1], nd[p << 1 | 1]);
}
//套路区间查询
inline LineBase Query(int p, int pl, int pr, int l, int r) {
if (pl == l && pr == r) return nd[p];
register int mid = pl + pr >> 1;
if (mid >= r) return Query(p << 1, pl, mid, l, r);
else if (mid + 1 <= l) return Query(p << 1 | 1, mid + 1, pr, l, r);
else return Merge(Query(p << 1, pl, mid, l, mid), Query(p << 1 | 1, mid + 1, pr, mid + 1, r));
}
};
Segtree sgt;
//用来维护差分数组的树状数组
inline int LowBit(int x) {
return x & -x;
}
inline void Update(int i, int x) {
for (register int j = i;j <= n;j += LowBit(j)) c[j] ^= x;
}
inline int Query(int i) {
register int ans = 0;
for (register int j = i;j >= 1;j -= LowBit(j)) ans ^= c[j];
return ans;
}
inline void Read() {
n = qread(); m = qread();
for (register int i = 1;i <= n;i++) a[i] = qread();
}
inline void Solve() {
for (register int i = n;i >= 2;i--) a[i] ^= a[i - 1];
for (register int i = 1;i <= n;i++) Update(i, a[i]);
sgt.Init(1, 1, n);
for (register int i = 1;i <= m;i++) {
register int opt = qread(), l = qread(), r = qread(), v = qread();
if (opt == 1) { // 修改操作,修改位置l和位置r+1
Update(l, v);
sgt.Modify(1, 1, n, l, v);
if (r < n) {
Update(r + 1, v);
sgt.Modify(1, 1, n, r + 1, v);
}
} else {//查询操作
register int k = Query(l);//求a[l]的值
if (l == r) {
printf("%d\n", Max(k ^ v, v));//特判l=r的情况,这时只有2种可能,讨论即可
} else {
LineBase cur = sgt.Query(1, 1, n, l + 1, r);//取出线性基
cur.Insert(k);
//在线性基里面求最大
for (register int i = 29;i >= 0;i--) {
if ((v ^ cur.a[i]) > v) v = (v ^ cur.a[i]);
}
printf("%d\n", v);
}
}
}
}
int main() {
Read();
Solve();
#ifndef ONLINE_JUDGE
while (1);
#endif
return 0;
}//完结撒花~