[bzoj4184]shallot
刚学了一下来捉这题。。
维护一个集合,支持插入,删除,询问当前集合中任取数异或和最大值。(实际上每次插入删除都询问)
题里没说不过好像不会插入重复的数,,,这并不影响什么就是了。可能代码比较好写,,
我们知道肯定是要用线性基来解决的,但是不支持删除。
所以我们换个思路,如果对于每次暴力跑线性基求的话会有很多重复的计算。而线性基的求解与顺序无关,所以把重复的数部分的线性基先求出来就能减少运算了。
而且一个数存在的时间是一个区间,所以对于时间维护一棵线段树,每个节点存储在这个节点代表的时间区间里全都有,而且和兄弟节点相比特有的所有值。这样只要dfs一遍线段树,一直记录根到当前节点所有数的线性基,一直到叶子就得到答案了。
不明白我上面这些在说啥也无所谓,做法就是这样的:
我们用set或者map就可以简单的维护一个数插入删除的时间。
我们维护一棵关于时间的线段树,给线段树上每个节点开一个vector。
一个数存在的时间如果是$[l,r]$,就用类似区间打标记的方式把这个数插入线段树上对应着它存在时间的节点的vector里。(这步是$O(nlogn)$的)
对线段树进行dfs,并且维护根到节点所有vector中存的数的线性基。到叶子直接利用线性基求出最大值就好了。(应该是$O(nlog^2 n)$的吧)
#include<bits/stdc++.h> using namespace std; const int N=500010; inline int read(){ int r=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){r=r*10+c-'0',c=getchar();} return r*f; } struct Node{ int l,r; vector<int>s; }T[N<<2]; struct gay{ int g[31]; gay(){ memset(g,0,sizeof g); } void ins(int x){ for(int i=30;~i;i--){ if(!(x>>i))continue; if(!g[i]){ g[i]=x;break; } x^=g[i]; } } }gg; int n,a[N],r[N]; map<int,int>mp; #define ls o<<1 #define rs o<<1|1 #define L T[o].l #define R T[o].r #define M (L+R>>1) void build(int o,int l,int r){ T[o]=(Node){l,r}; if(l==r)return; build(ls,L,M); build(rs,M+1,R); } int ql,qr,w; void upd(int o){ if(ql<=L&&R<=qr){ T[o].s.push_back(w); return; } if(ql<=M)upd(ls); if(qr>M) upd(rs); } void init(){ n=read(); build(1,1,n); for(int i=1;i<=n;i++){ a[i]=read(); if(a[i]>0)mp[a[i]]=i; else{ r[mp[-a[i]]]=i-1; } } for(int i=1;i<=n;i++) if(a[i]>0){ ql=i,qr=r[i]?r[i]:n; w=a[i];upd(1); } } void solve(int o,gay now){ for(int i=0;i<T[o].s.size();i++) now.ins(T[o].s[i]); if(L==R){ int ans=0; for(int i=30;~i;i--) ans=max(ans,ans^now.g[i]); printf("%d\n",ans); return; } solve(ls,now);solve(rs,now); } int main(){ init(); solve(1,gg); }
叫线性gay叫多了代码里就用gay来表示了+_+