线性基补充(删除操作以及区间操作)

带删除的线性基

离线删除操作,维护线性基中每个元素的最晚删除时间。
在这篇里面有讲。

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 ai1
那么询问 [ l , r ] [l,r] [l,r] a l ∼ r a_{l\sim r} alr的线性基相当于查询 a l a_l al b l + 1 ∼ r b_{l+1\sim r} bl+1r组成的线性基,易证这两个组是等价的。

于是要维护 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);
		}
	}
}

你可能感兴趣的:(线性基)