大致题意: 让你支持三种操作:加入一个数,删除一个数,求选出任意数的最大异或和。
动态线性基
首先看到选出任意数的最大异或和,肯定会想到线性基。
然后再看看它要我们支持加入和删除,加入还好,删除是什么鬼东西?
于是就需要请出我们的动态线性基了。
实际上,根据常规套路,很多动态的问题都可以转化为离线搞,这题也不例外。
线段树分治
线段树分治是一个很有意思的算法。
这题中,我们可以离线求出每个数的出现时间区间,然后对线段树上表示这一区间的节点们开个\(vector\)扔入这个数。
询问时我们遍历线段树,对每一层开一个线性基,每个线性基从父节点那里继承过来,然后再往线性基中插入该节点上的数。
容易发现每个叶节点上维护的正是这一时间存在的所有数。
于是这道题就做完了,代码也是很简洁的。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define LN 25
#define LV 30
using namespace std;
int n;map<int,int> p;
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int f,T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0,f=1;W(!D) f=c^'-'?1:-1;W(x=tn+(c&15),D);x*=f;}
Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('\n');}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
class LinearBasis//线性基
{
private:
int v[LV+5];
public:
I void Ins(RI x) {for(RI i=LV;~i;--i) if(x>>i&1) {if(!v[i]) return (void)(v[i]=x);x^=v[i];}}//插入
I int Qry(RI t=0) {for(RI i=LV;~i;--i) t<(t^v[i])&&(t^=v[i]);return t;}//询问
};
class SegmentTree//线段树
{
private:
#define PT CI l=1,CI r=n,CI rt=1
#define LT l,mid,rt<<1
#define RT mid+1,r,rt<<1|1
LinearBasis B[LN+5];vector<int> V[N<<2];vector<int>::iterator it;
public:
I void Add(CI x,CI L,CI R,PT)//往一个区间内扔入x
{
if(L<=l&&r<=R) return V[rt].push_back(x);int mid=l+r>>1;
L<=mid&&(Add(x,L,R,LT),0),R>mid&&(Add(x,L,R,RT),0);
}
I void Solve(CI d=0,PT)//线段树分治
{
for(it=V[rt].begin();it!=V[rt].end();++it) B[d].Ins(*it);//插入该节点上的数
if(l==r) return F.writeln(B[d].Qry());int mid=l+r>>1;//边界直接询问输出
B[d+1]=B[d],Solve(d+1,LT),B[d+1]=B[d],Solve(d+1,RT);//把线性基继承给儿子
}
}S;
int main()
{
RI i,x;for(F.read(n),i=1;i<=n;++i) F.read(x),x>0?p[x]=i:(x=-x,S.Add(x,p[x],i-1),p.erase(x),0);//开map记录一个数上次出现位置
for(map<int,int>::iterator it=p.begin();it!=p.end();++it) S.Add(it->first,it->second,n);//把没被删掉过的数扔入线段树
return S.Solve(),F.clear(),0;
}