BZOJ4184:shallot(线段树分治,线性基)
Description
小苗去市场上买了一捆小葱苗,她突然一时兴起,于是她在每颗小葱苗上写上一个数字,然后把小葱叫过来玩游戏。
每个时刻她会给小葱一颗小葱苗或者是从小葱手里拿走一颗小葱苗,并且
让小葱从自己手中的小葱苗里选出一些小葱苗使得选出的小葱苗上的数字的异或和最大。
这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为Oi选手的你,你能帮帮他吗?
你只需要输出最大的异或和即可,若小葱手中没有小葱苗则输出0。
Input
第一行一个正整数n表示总时间;第二行n个整数a1,a2...an,若ai大于0代表给了小葱一颗数字为ai的小葱苗,否则代表从小葱手中拿走一颗数字为-ai的小葱苗。
Output
输出共n行,每行一个整数代表第i个时刻的最大异或和。
Sample Input
6
1 2 3 4 -2 -3
1 2 3 4 -2 -3
Sample Output
1
3
3
7
7
5
3
3
7
7
5
HINT
N<=500000,Ai<=2^31-1
Solution
可以发现每个数存在的时间是一个区间,那么我们可以以时间为下标建线段树,把每个数存在的时间拆分成若干区间扔到线段树对应节点上。
最后遍历一遍线段树,同时利用线性基查询异或最大值。遍历的时候用一个结构体更方便线性基的回溯。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<vector> 5 #include<map> 6 #define N (500009) 7 using namespace std; 8 9 struct Basis{int d[31];}LB; 10 int n,m,a[N],maxn,ans[N]; 11 map<int,int>L,R; 12 vector<int>v[N<<2]; 13 14 inline int read() 15 { 16 int x=0,w=1; char c=getchar(); 17 while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();} 18 while (c>='0' && c<='9') x=x*10+c-'0', c=getchar(); 19 return x*w; 20 } 21 22 void Update(int now,int l,int r,int l1,int r1,int x) 23 { 24 if (l>r1 || r<l1) return; 25 if (l1<=l && r<=r1) {v[now].push_back(x); return;} 26 int mid=(l+r)>>1; 27 Update(now<<1,l,mid,l1,r1,x); Update(now<<1|1,mid+1,r,l1,r1,x); 28 } 29 30 void Query(int now,int l,int r,Basis LB) 31 { 32 for (int i=0; i<v[now].size(); ++i) 33 for (int j=30; j>=0; --j) 34 if (v[now][i]&(1<<j)) 35 { 36 if (!LB.d[j]) {LB.d[j]=v[now][i]; break;} 37 v[now][i]^=LB.d[j]; 38 } 39 if (l==r) 40 { 41 int now=0; 42 for (int i=30; i>=0; --i) now=max(now,now^LB.d[i]); 43 ans[l]=now; return; 44 } 45 int mid=(l+r)>>1; 46 Query(now<<1,l,mid,LB); Query(now<<1|1,mid+1,r,LB); 47 } 48 49 int main() 50 { 51 n=read(); 52 for (int i=1; i<=n; ++i) a[i]=read(); 53 for (int i=n; i>=1; --i) 54 if (a[i]>0) 55 { 56 if (R[a[i]]==0) R[a[i]]=n; 57 L[a[i]]=i; 58 Update(1,1,n,L[a[i]],R[a[i]],a[i]); 59 } 60 else R[-a[i]]=i-1; 61 Query(1,1,n,LB); 62 for (int i=1; i<=n; ++i) printf("%d\n",ans[i]); 63 }