线性基

带删除线性基

  • 强制在线

用线段树配合线性基合并不难做到 $O(k^2\log n)$,在此不叙。

我们维护一个不经消元的线性基,以及所有不在线性基内的数。

假设我们要删除 $u$。

如果 $u$ 不在线性基内,直接删掉即可。

若 $u$ 恰好在线性基内,则需要进行讨论。

对于某个不在线性基中的数 $x$,必有且仅有一个线性基的子集 $S$ 异或和为 $x$ ,记为 $S_x$。

假如线性基外的某个数 $x$ 的 $S_x$ 中包含 $u$,那么就说明 $S_x,x$ 是线性相关的。

此时用 $x$ 替换 $u$ 并将 $u$ 删除即可。(若找不到这个 $x$ 就直接删除 $u$)

然后要维护 $S$ ,由于 $u$ 被新的 $x∩(S_x-u)$ 代替了,而且原来 $u$ 的位置被 $x$ 占据了,这相当于让所有含有 $u$ 的 $S_y$ 异或上 $(S_x-u)$。

由于原来是 $u$ 现在是 $x$ 这一位没有变,我们可以把“让所有含有 $u$ 的 $S_y$ 异或上 $(S_x-u)$”这条指令拆分成 $O(k)$ 条“让所有含有 $u$ 的 $S_y$ 的某一位取反”这样的指令。

咕。

  • 离线

显然可以套用线段树分治得到 $O(k\log n)$ 的复杂度。

实际上可以做到 $O(k)$ 。

对于每个元素,记下其删除时间。

先考虑不经消元的线性基。

当加入一个新向量时,若和旧的基线性无关,则直接加入。

若和旧的基线性有关,则可以在线性有关的一组向量中换掉一个。

类似动态生成树等问题,我们尝试用删除时间晚的去换早的。这称作最大权线性基

然而每次都要消元,复杂度 $O(k^2)$ ,并不优越。


考虑沿用前面的思路削成三角基。设 $h_x$ 为 $x$ 的删除时间。

我们尝试构造一种方案,使得消元之后,线性基中的 $h$ 不会变差。

在消元时,$\{x,y\}$ 可以被 $\{x,x\ {\rm xor}\ y\}$ 代替。若本体 $x,y$ 的其中一个被删去,则由它们产生的 $x\ {\rm xor}\ y$ 也会失效,即 $h_{x\ {\rm xor}\ y}=\min(h_x,h_y)$。

这样,不难想到将基按照 $h_x$ 从大到小排序,然后用前面的消后面的。这样,所有位置上的 $h$ 都不会变小。


以上是把任意基变成三角基的 $O(k^2)$ 消元,接着考虑增量法。

我们让已有的三角基以 $h$ 为序,若 $u$ 和基线性无关,则可加入。

将 $u$ 以插入排序的方法加入,然后消元。不难发现,只有可能从 $u$ 向后消(传火?),这样就是每次 $O(k)$ 的了。


还有一种市面上流传较广的做法 :

在 $u$ 加入(三角)线性基时,可能需要与某个向量 $a$ 异或($u,a$ 的某一位都为 $1$)。

此时,若 $h_u>h_a$ 我们用 $u$ 替换 $a$。拿 $u\ {\rm xor}\ a$ 继续消。

——Command_block's Blog

由于在线的我还没有遇到过题目,所以就讲一下离线的吧。
具体地,你需要:

维护一个支持加入、删除操作的集合的每时每刻线性基(为了方便起见,的大小)。(保证加入一个数时集合中无此数,删除一个数时集合中有此数)

我们可以预处理 \(ddl_i\) 表示询问 \(i\)(一个插入操作)的 deadline(最后还活着的一天)。把所有插入询问离线下来,依次将其插入线性基,线性基记录该位置的数 \(v_i\) 及其 deadline \(t_i\)。记插入的数为 \(x\),deadline 为 \(tim\),若 \(tim>t_i\),则将 \(swap:(x,tim)\&(v_i,t_i)\),并继续下去,非常巧妙哈。复杂度 \(O(qk)\)\(k=\log V\)

这里有一道模板题。

m q op[i] x[i] 已知,\(m\le 30,q\le 10^5,0\le x[i]< 2^m\))给你一个无向图 \(G=(V,E)\) 和初始为空的数集 \(S\),初始时 \(V=[0,2^m),E=\varnothing\)。现在给你 \(q\) 次操作,形如:将 \(x\)\(S\) 中插入/删除,每个时刻图中的边为 \(\{(u,v)\mid u\text{ xor }v\in S\}\),求无向图最大连通块点数。

解:不难发现 \(x,y\) 联通当且仅当 \(x\text{ xor }y\) 能由 \(S\) 中若干元素异或而来,因此答案即为 2 的【\(S\) 的线性基大小】次方。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5,K=32;
int m,q,v[K],t[K],a[N],ddl[N];
map<int,int>M;
inline int read(){
	register char ch=getchar();register int x=0;
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
void insert(int val,int tim){
	for(int i=30;~i;i--)
		if((val>>i)&1){
			if(!v[i]){v[i]=val;t[i]=tim;return;}
			if(t[i]<tim)swap(t[i],tim),swap(v[i],val);
			val^=v[i];
		}
}
int ask(int tim){
	int cnt=0;
	for(int i=30;~i;i--)if(v[i]&&t[i]>=tim)cnt++;
	return cnt;
}
int main(){
    freopen("xor.in","r",stdin);freopen("xor.out","w",stdout);
	m=read(),q=read();
	for(int i=1;i<=q;i++)ddl[i]=q;
	for(int i=1;i<=q;i++){
		int op=read();
		if(op==1)a[i]=read(),M[a[i]]=i;
		else op=read(),ddl[M[op]]=i-1;
	}
	for(int i=1;i<=q;i++){
		if(a[i])insert(a[i],ddl[i]);
		cout<<(1<<ask(i))<<'\n';
	}
}

前缀线性基

struct Prefix_Linear_Basis{
	ll p[max_wei+1];int pos[max_wei+1];
	inline void init(){memset(p,0,sizeof p);memset(pos,0,sizeof pos);}
	inline void insert(Prefix_Linear_Basis A,int w,ll val){
		*this=A;
		for(int i=max_wei;i>=0;i--)if(val>>i&1){
			if(!p[i]){p[i]=val,pos[i]=w;return;}
			else if(pos[i]<w){swap(pos[i],w);swap(p[i],val);}
			val^=p[i];
		}
	}
	inline ll Query(int l){
		//查询此基中所有≥l的最大值
		ll ans=0;
		for(int i=max_wei;i>=0;i--)
			if(p[i]&&pos[i]>=l)ans=max(ans,ans^p[i]);
		return ans; 
	}
}a[maxn];
posted @ 2022-03-18 16:51  pengyule  阅读(270)  评论(0)    收藏  举报