BZOJ4184: shallot
线性基·线段树
题解:
又是一道神题!
然而蒟蒻只能抄题解,还抄漏了一句。。。
一个数字的出现时间是一段区间
[l,r]
,可以开一棵线段树,把
[l,r]
对应的线段树节点上的vector里加上这个数。
最后dfs一次,参数传一个线性基(不是引用),把当前点vector里的都加进去,再往下dfs。
这样到达叶节点的线性基就包含了这个点时存在的数,输出一下XOR最大值即可。
也可以不实际开出线段树来,采用一种类似分治的方式。
原理一样,详见代码。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int LOG = 30;
const int N = 500005;
int n;
map<int,int> last;
inline void read(int &num){
num=0; char c; int bo=1;
while(!isdigit(c=getchar()) && c!='-');
if(c=='-') bo=-1;
else num=c-'0';
while(isdigit(c=getchar())) num=num*10+c-'0';
num*=bo;
}
struct Data{
int l,r,x;
Data(int _l,int _r,int _x):l(_l),r(_r),x(_x){}
};
struct LB{
int b[LOG+1];
LB(){ memset(b,0,sizeof(b)); }
void insert(int x){
for(int i=LOG;i>=0;i--){
if((x>>i)&1){
if(!b[i]){ b[i]=x; break; }
x^=b[i];
}
}
}
int getMax(){
int ans=0;
for(int i=LOG;i>=0;i--){
if((ans^b[i])>ans) ans^=b[i];
}
return ans;
}
};
void work(int l,int r,vector<Data> d,LB lb){
if(l>r) return;
// D(l); D(r); E;
for(int i=0;i<d.size();i++){
if(d[i].l==l && d[i].r==r){
lb.insert(d[i].x);
}
}
if(l==r){
printf("%d\n",lb.getMax());
return;
}
int mid=(l+r)>>1;
vector<Data> ld,rd;
for(int i=0;i<d.size();i++){
if(d[i].l==l && d[i].r==r) continue; //漏了这一句,TLE了半天。。。
if(d[i].l<=mid) ld.push_back(Data(d[i].l,min(mid,d[i].r),d[i].x));
if(d[i].r>mid) rd.push_back(Data(max(mid+1,d[i].l),d[i].r,d[i].x));
}
work(l,mid,ld,lb);
work(mid+1,r,rd,lb);
}
int main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
read(n); int a;
vector<Data> d; LB lb;
for(int i=1;i<=n;i++){
read(a);
if(a>0) last[a]=i;
else{ a=-a; d.push_back(Data(last[a],i-1,a)); last[a]=0; }
}
for(map<int,int>::iterator it=last.begin();it!=last.end();it++){
if(it->second){
d.push_back(Data(it->second,n,it->first));
}
}
work(1,n,d,lb);
}