原题链接:https://www.luogu.com.cn/problem/P5607
无力回天
题目描述
给你一个长度为 n 的整数序列 a 1 a_1 a1 , a 2 a_2 a2 , … \ldots …, a n a_n an ,你需要实现以下两种操作,每个操作都可以用四个整数 o p t l r v opt\;l\;r\;v optlrv 来表示:
o p t = 1 opt=1 opt=1 时,代表把一个区间 [ l , r ] [l,r] [l,r] 内的所有数都 xor 上 v v v。
o p t = 2 opt=2 opt=2 时, 查询一个区间 [ l , r ] [l,r] [l,r] 内选任意个数(包括 0 0 0 个)数 xor 起来,这个值与 v v v 的最大 xor 和是多少。
输入格式
第一行有两个正整数 n , m n,m n,m。 第二行有 n n n 个整数表示给你的序列。 之后 m m m 行每行有四个整数 o p t , l , r , v opt, l, r, v opt,l,r,v表示一个操作。
输出格式
对于每个 o p t = 2 opt=2 opt=2 的操作,输出一行一个数表示答案~
输入输出样例
输入 #1
4 5
1 14 51 4
2 1 3 0
1 2 3 3
2 1 4 10
1 1 4 514
2 3 4 2
输出 #1
61
63
560
说明/提示
Idea:nzhtl1477,Solution:nzhtl1477,Code:nzhtl1477,Data:nzhtl1477
对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 5 × 1 0 4 1 \le n , m \le 5 \times 10^4 1≤n,m≤5×104 ,值域在 [ 0 , 1 0 9 ] [0,10^9] [0,109] 之间
题解
好家伙,做省选数据结构本来是想复习数据结构,结果都是加一些非数据结构骚操作。
通过回忆,要求最大异或和,需要一个叫做线性基的东西,而线性基的大小是 O ( l o g 2 值 域 大 小 ) O(log_2值域大小) O(log2值域大小)的,对于这道题就是 30 30 30,那么自然想到可以通过线段树来维护区间的线性基,复杂度为 O ( n l o g 2 n l o g 2 2 值 域 大 小 ) O(nlog_2nlog_2^2值域大小) O(nlog2nlog22值域大小)三个log满足时限。

但是问题来了,本题还有区间异或的操作,因为 v , a 1 , a 2 ⋯ , a n v,a_1,a_2\cdots ,a_n v,a1,a2⋯,an的线性基不一定等于 a 1 ⊕ v , a 2 ⊕ v , ⋯ , a n ⊕ v a_1\oplus v, a_2 \oplus v,\cdots,a_n\oplus v a1⊕v,a2⊕v,⋯,an⊕v的线性基,所以打标记区间修改的思路不成了。我们注意到,区间修改不容易解决,但是单点修改的话就只需要合并线性基就行了。区间转单点,就会想到差分的思路。再深入考察,发现异或运算是与自己可逆的(即 a ⊕ b ⊕ b = a ) a \oplus b \oplus b =a) a⊕b⊕b=a),同时满足交换律结合律,所以差分就有了实现的可能。
设 b i = a i − 1 ⊕ a i b_i=a_{i-1}\oplus a_i bi=ai−1⊕ai,则满足如下性质:
a i = b 1 ⊕ b 2 ⊕ ⋯ ⊕ b i a_i=b_1\oplus b_2\oplus\cdots\oplus b_i ai=b1⊕b2⊕⋯⊕bi
且 b l + 1 ∼ b r b_{l+1}\sim b_r bl+1∼br是由 a l ∼ a r a_l\sim a_r al∼ar两两异或得到的,所以 a l , b l + 1 ∼ b r a_l,b_{l+1}\sim b_r al,bl+1∼br的线性基与 a l ∼ a r a_l\sim a_r al∼ar的线性基相同。于是就有了最终的解决方案:用线段树维护 { b i } \{b_i\} { bi}的线性基,然后线段树/树状数组维护 { b i } \{b_i\} { bi}。询问区间 [ l , r ] [l,r] [l,r]时,查询 b l + 1 ∼ r b_{l+1\sim r} bl+1∼r的线性基,再求 b 1 ∼ l b_{1\sim l} b1∼l的异或和得到 a l a_l al加入线性基,之后按位贪心就能得答案;修改就是基本的差分数列修改。
代码
#include<bits/stdc++.h>
#define ls v<<1
#define rs v<<1|1
#define lb(v) v&(-v)
using namespace std;
const int M=5e4+5;
struct node{
int base[35];}tree[M<<2];
int n,m,que[M],xsum[M];
void xadd(int v,int x){
for(;v<=n;v+=lb(v))xsum[v]^=x;}
int xask(int v){
int r=0;for(;v;v-=lb(v))r^=xsum[v];return r;}
node up(node a,node b)
{
for(int i=0;i<=30;++i)
for(int j=30;j>=0;--j)
if(a.base[i]&(1<<j))
if(b.base[j])a.base[i]^=b.base[j];
else {
b.base[j]=a.base[i];break;}
return b;
}
void build(int v,int le,int ri)
{
if(le==ri){
for(int i=30;i>=0;--i)if(que[le]&(1<<i)){
tree[v].base[i]=que[le];break;}return;}
int mid=le+ri>>1;
build(ls,le,mid);build(rs,mid+1,ri);
tree[v]=up(tree[ls],tree[rs]);
}
void modify(int v,int le,int ri,int x,int val)
{
if(le==ri)
{
memset(tree[v].base,0,sizeof(tree[v].base));
for(int i=30;i>=0;--i)if(val&(1<<i)){
tree[v].base[i]=val;break;}
return;
}
int mid=le+ri>>1;
if(x<=mid)modify(ls,le,mid,x,val);
else modify(rs,mid+1,ri,x,val);
tree[v]=up(tree[ls],tree[rs]);
}
node ask(int v,int le,int ri,int ll,int rr)
{
if(ll<=le&&ri<=rr)return tree[v];
int mid=le+ri>>1;
node r;memset(r.base,0,sizeof(r.base));
if(ll<=mid)r=ask(ls,le,mid,ll,rr);
if(mid<rr)r=up(r,ask(rs,mid+1,ri,ll,rr));
return r;
}
void in()
{
scanf("%d%d",&n,&m);
for(int i=1,a=0,b;i<=n;++i)scanf("%d",&b),xadd(i,que[i]=a^b),a=b;
}
void ac()
{
build(1,1,n);
node b;
for(int op,l,r,v,a;m--;)
{
scanf("%d%d%d%d",&op,&l,&r,&v);
if(op-1)
{
b=ask(1,1,n,l+1,r);
a=xask(l);
for(int i=30;i>=0;--i)
if(a&(1<<i))
if(b.base[i])a^=b.base[i];
else {
b.base[i]=a;break;}
for(int i=30;i>=0;--i)v=max(v,v^b.base[i]);
printf("%d\n",v);
}
else xadd(l,v),xadd(r+1,v),modify(1,1,n,l,que[l]^=v),modify(1,1,n,r+1,que[r+1]^=v);
}
}
int main()
{
in(),ac();
system("pause");
}