离线删除操作,维护线性基中每个元素的最晚删除时间。
在这篇里面有讲。
Code:
#include
#define maxn 2000005
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,op[maxn],a[maxn],d[35],sz,del[maxn],tim[35];
map<int,int>last;
inline void ins(int x,int t){
for(int i=29;i>=0;i--)
if(x>>i&1){
if(d[i]) {if(t>tim[i]) swap(x,d[i]),swap(t,tim[i]); x^=d[i];}
else {d[i]=x,tim[i]=t,sz++; return;}
}
}
inline void erase(int t){
for(int i=29;i>=0;i--)
if(tim[i]==t) {d[i]=tim[i]=0,sz--;return;}
}
int main()
{
read(n),read(m);
for(int i=1;i<=m;i++){
read(op[i]),read(a[i]);
if(op[i]==1) last[a[i]]=i,del[i]=m+1;
else del[last[a[i]]]=i;
}
int ans=0;
for(int i=1;i<=m;i++){
if(op[i]==1) ins(a[i],del[i]);
else erase(i);
ans^=1<<(n-sz);
}
printf("%d\n",ans);
}
例:CF1100F Ivan and Burgers,区间查询最大异或和
扫描 r r r,维护线性基中每个元素的最大左端点 l l l。与删除操作类似。
这个可以强制在线,把每个 r r r的线性基存下来即可。
复杂度 O ( n log n ) O(n\log n) O(nlogn)
Code:
#include
#define maxn 500005
using namespace std;
int n,m,a[maxn],d[20],pos[20],ans[maxn];
struct node{
int l,r,id;
bool operator < (const node &p)const{return r<p.r;}
}q[maxn];
void insert(int x,int t){
for(int i=19;i>=0;i--) if(x>>i&1){
if(d[i]) {if(t>pos[i]) swap(t,pos[i]),swap(x,d[i]); x^=d[i];}
else {d[i]=x,pos[i]=t; return;}
}
}
int qry(int t){
int ret=0;
for(int i=19;i>=0;i--) if(pos[i]>=t&&!(ret>>i&1)) ret^=d[i];
return ret;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+1+m);
for(int i=1,j=1;i<=n;i++){
insert(a[i],i);
for(;j<=m&&q[j].r<=i;j++) ans[q[j].id]=qry(q[j].l);
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
例:CF587E Duff as a Queen,区间修改查询区间线性基大小。
线性基不支持区间修改,所以用到一个常见套路:做差分,设 b i = a i x o r a i − 1 b_i=a_i~xor~a_{i-1} bi=ai xor ai−1
那么询问 [ l , r ] [l,r] [l,r]的 a l ∼ r a_{l\sim r} al∼r的线性基相当于查询 a l a_l al与 b l + 1 ∼ r b_{l+1\sim r} bl+1∼r组成的线性基,易证这两个组是等价的。
于是要维护 b b b的区间线性基,单点修改,这个用线段树,合并的时候暴力插入,复杂度 O ( n log n log 2 W ) O(n\log n\log^2W) O(nlognlog2W)
还要维护 a a a的单点值,区间修改,这个树状数组就可以了。
因为插入的时候先检验如果大小达到了30直接return,这样似乎会快一些。
Code:
#include
#define maxn 200005
using namespace std;
struct Base{
int a[31],cnt;
void clear(){memset(a,0,sizeof a),cnt=0;}
void ins(int x){
if(cnt==30) return;
for(int i=29;i>=0&&x;i--) if(x>>i&1){
if(a[i]) x^=a[i];
else {a[i]=x,cnt++;return;}
}
}
void merge(const Base &B){
for(int i=29;i>=0;i--) if(B.a[i]) ins(B.a[i]);
}
}t[maxn<<2];
int n,m,a[maxn],b[maxn];
int arr[maxn];
void upd(int i,int v){for(;i<=n;i+=i&-i) arr[i]^=v;}
int qxor(int i){int s=0;for(;i;i-=i&-i) s^=arr[i];return s;}
void upd(int i){t[i]=t[i<<1],t[i].merge(t[i<<1|1]);}
void build(int i,int l,int r){
if(l==r) return t[i].ins(b[l]);
int mid=(l+r)>>1;
build(i<<1,l,mid),build(i<<1|1,mid+1,r);
upd(i);
}
void mdf(int i,int l,int r,int x){
if(l==r) {t[i].clear(),t[i].ins(b[x]);return;}
int mid=(l+r)>>1;
x<=mid?mdf(i<<1,l,mid,x):mdf(i<<1|1,mid+1,r,x);
upd(i);
}
void qry(int i,int l,int r,int x,int y){
if(x<=l&&r<=y) return t[0].merge(t[i]);
int mid=(l+r)>>1;
if(x<=mid) qry(i<<1,l,mid,x,y);
if(y>mid) qry(i<<1|1,mid+1,r,x,y);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]^a[i-1],upd(i,b[i]);
build(1,1,n);
for(int op,l,r,x;m--;){
scanf("%d%d%d",&op,&l,&r);
if(op==1){
scanf("%d",&x);
upd(l,x),upd(r+1,x),b[l]^=x,b[r+1]^=x;
mdf(1,1,n,l); if(r<n) mdf(1,1,n,r+1);
}
else{
t[0].clear(),t[0].ins(qxor(l)); if(l<r) qry(1,1,n,l+1,r);
printf("%d\n",1<<t[0].cnt);
}
}
}