线性基
带删除线性基
- 强制在线
用线段树配合线性基合并不难做到 $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];

浙公网安备 33010602011771号