Operation
题解:看到区间最大异或和,首先想到的是线性基;(读题发现要用到上一次的结果,也就是要强制在线,然后自己刚学完主席树就想是不是主席树套线性基,但是这是会超时的)
线性基可以处理的操作是:
- 在数列末尾插入一个数
- 查询全局的子集异或最大值
由于线性基的长度很短,因此我们可以将数列所有前缀的线性基保存下来。1到x的线性基可以由1到x-1的线性基通过插入a[x]来求得,这样,我们就可以查询前缀区间的子集异或最大值。现在问题的关键在于,查询区间 [L, R] 时,如何避免 [1, L-1] 的干扰。
考虑线性基的插入过程,如果线性基当前位上已经有值,我们就不能把待插入的值放入这一位,因此线性基上每一位的数,都是对应位上在原数列最左侧的数字。现在我们改变策略,使得线性基上每一位的数,都变成对应位上在原数列最右侧的数字。实现这个策略的方法是:我们额外保存线性基上每一位数在原数列中的位置,插入的时候,如果对应位上的数在原数列中更靠左,就用待插入的数和它交换。基于这种策略,我们在查询区间 [L, R] 时,可以在区间 [1, R] 对应的线性基中查询,对于线性基上每一位的数,如果它在原数组中出现的位置比 L 更靠右,就考虑它对答案的贡献,否则直接跳过这一位。
这个做法的正确性也很显然,通过改变策略,使线性基上每一位数变成对应位上在原数列最右侧的数字,可以看成线性基插入数字的顺序变反,完全不影响线性基的性质。同时,将线性基上所有在原数组中的位置比 x 更靠左的数字删除,可以视为区间 [1, L-1] 的数字还没有被插入线性基。
复杂度:O((n + m) logx),n为初始数列长度,m为操作次数,x为值域大小。
大佬的博客讲解:here
AC_Code:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 typedef long long ll; 6 #define endl ' ' 7 const int maxn = 35; 8 const int maxm = 5e5+5; 9 const int inf = 0x3f3f3f3f; 10 11 int cnt;//当前已插入的数的个数 12 int a[maxm][maxn];//保存所有前缀区间的线性基 13 int b[maxm][maxn];//保存线性基上的数字在原数组上的对应位置 14 int n,m; 15 16 void LB(int x){ 17 int cur=++cnt;//表示待插入的数字在原数组上的位置 18 for(int i=31;i>=0;i--){ 19 a[cnt][i]=a[cnt-1][i]; 20 b[cnt][i]=b[cnt-1][i]; 21 } 22 for(int i=31;i>=0;i--){ 23 if( !(x>>i) ) continue; 24 if( !a[cnt][i] ){ 25 a[cnt][i]=x; 26 b[cnt][i]=cur; 27 break; 28 } 29 else{ 30 if( cur>b[cnt][i] ){ //如果待插入的数字在原数组上更靠右,则用线性基上的数与其交换 31 swap(a[cnt][i],x); 32 swap(b[cnt][i],cur); //位置也要交换 33 } 34 x^=a[cnt][i]; 35 } 36 } 37 } 38 39 int query(int l,int r){ 40 l=l%cnt+1; r=r%cnt+1; //注意这里是%cnt,不是%n 41 if( l>r ) swap(l,r); 42 int ret=0; 43 for(int i=31;i>=0;i--){ 44 if( b[r][i]>=l ){ //如果在原数组中的位置比l更靠右,那么就产生贡献,此处b[r][i]就已经限制了右区间 45 ret=max(ret,ret^a[r][i]); //线性基贪心求最大值的基本操作 46 } 47 } 48 return ret; 49 } 50 51 52 53 int main() 54 { 55 int t; scanf("%d",&t); 56 while( t-- ){ 57 cnt=0; 58 scanf("%d%d",&n,&m); 59 for(int i=0;i<n;i++){ 60 int a; 61 scanf("%d",&a); 62 LB(a); 63 } 64 int lastans=0, opt, x, y; //lastans用于处理强制在线 65 for(int i=0;i<m;i++){ 66 scanf("%d%d",&opt,&x); 67 if( opt==0 ){ 68 scanf("%d",&y); 69 x=x^lastans; 70 y=y^lastans; 71 lastans = query(x, y); 72 printf("%d ",lastans); 73 } 74 else{ 75 LB(x^lastans); 76 } 77 } 78 } 79 return 0; 80 }