[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
Sample Output
1
3
3
7
7
5
Hint
N<=500000,Ai<=2^31-1
比较基础的线段树分治练习题.
按照修改时间的区间分治.
维护一个线性基,但是线性基不支持删除元素,于是递归的时候存在栈里就可以了.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define mid ((l+r)>>1)
#define lc (rt<<1)
#define rc ((rt<<1)|1)
#define maxn 500005
using namespace std;
int cnt,n,len;
int b[maxn],seq[maxn],nxt[maxn],head[maxn],ans[maxn];
vector<int> seg[maxn<<2];
struct number{int x,l,r;}p[maxn];
struct node
{
int num,base[33];
void insert(int x)
{
if(num==32)return;
for(int i=31;~i;i--)
if(x&(1<<i))
{
if(!base[i]){base[i]=x;++num;}
x^=base[i];
}
}
int getmax(int x){for(int i=31;~i;i--)x=max(x,x^base[i]);return x;}
}A;
void update(int rt,int l,int r,int L,int R,int x)
{
if(l>R||r<L)return;
if(L<=l&&r<=R){seg[rt].push_back(x);return;}
update(lc,l,mid,L,R,x);update(rc,mid+1,r,L,R,x);
}
void divide(int rt,int l,int r,node A)
{
for(int i=seg[rt].size()-1;A.num<32&&~i;i--)A.insert(seg[rt][i]);
for(int i=l;i<=r;i++)ans[i]=max(ans[i],A.getmax(0));
if(l==r)return;divide(lc,l,mid,A);divide(rc,mid+1,r,A);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&seq[i]);
for(int i=1;i<=n;i++)b[i]=abs(seq[i]);
sort(b+1,b+1+n);len=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++)
{
if(seq[i]>0)
{
int u=lower_bound(b+1,b+1+len,seq[i])-b;
nxt[++cnt]=head[u];head[u]=cnt;
p[cnt]=(number){seq[i],i,n};
}
else
{
int u=lower_bound(b+1,b+1+len,-seq[i])-b;
int j=head[u];p[j].r=i-1;head[u]=nxt[j];
}
}
for(int i=1;i<=cnt;i++)update(1,1,n,p[i].l,p[i].r,p[i].x);
divide(1,1,n,A);
for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
return 0;
}