神秘算法 —— 线性基求交
线性基求交:设 为两个线性基, 分别为其生成空间,则 是一个线性空间,称 与 两个线性基的交为 。
首先证明 是一个线性空间。其实很显然,对于任意 ,,同理 ,从而 。
以下是求线性基的交 的算法:
首先对线性基 进行一些调整(后面会讲),保持其生成空间 不变。
设存在一个线性基 ,满足以下三条性质:
说成人话,就是 是线性基 中与 有交的那部分, 中其余部分与 线性无关。
那么可以证明, 即为所求的线性基的交 。
分两步证明 :
- 假设存在 且 。
由于 ,故此时存在一些 满足 。将这些 分成两部分,前者 ,后者 ,令前者的异或和为 ,后者为 ,则 。
而 ,故 。又 ,由性质 3 得 ,从而 ,矛盾。 - 假设存在 且 。
组成 的基底既在 中也在 中,显然矛盾。
现在我们剩下的问题就剩下求出 了。
对每个 依次执行以下操作:
- 若 ,设其表示为 ,其中 ,令 ,并标记其为 类;
否则,不做任何操作,标记其为 类。
最终得到的 与 的交即为满足条件的 。以下证明其满足 :
发现经过前面的操作,所有 类的 都 ,所有 类的 都 。于是 类 所组成的集合即为 , 类即为 。
若存在 满足 且 ,则存在若干个 类 以及若干个 使得 ,取出这些 中下标最大的那个 ,则 ,与在位置 时 没有标记为 类矛盾!
证毕。
代码实现如下:
其中 tA
数组代表 所形成的线性基,tmp
数组代表线性基的每个位置上 中数贡献的异或和,即注意每个基底可表示为 ,tmp
存的即为 。新插入数时 u
为当前 剩下的值,cur
代表当前已异或上的 中的数,便于更新。
class LinearBase{
public:
ull base[64];
LinearBase() { memset(base,0,sizeof(base)); }
inline bool insert(ull x){
Rev(i,63,0) if((x>>i)&1) { if(base[i]) x^=base[i]; else { base[i]=x; For(j,i+1,63) if((base[j]>>i)&1) base[j]^=base[i]; ; For(j,0,i-1) if((base[i]>>j)&1) base[i]^=base[j]; ; return true; } } ; return false;
}
inline bool count(ull x){
Rev(i,63,0) if((x>>i)&1) { if(base[i]) x^=base[i]; else return false; } ; return true;
}
inline bool operator== (const LinearBase& rhs) { For(i,0,63) if(base[i]!=rhs.base[i]) return false; ; return true; }
friend inline LinearBase Union(const LinearBase& A,const LinearBase& B){
LinearBase C,tA=A; ull tmp[64]; memcpy(tmp,A.base,sizeof(tmp));
For(i,0,63) if(B.base[i]){
ull u=B.base[i],a=0; int ok=1;
Rev(j,i,0) if((u>>j)&1){
if(tA.base[j]) u^=tA.base[j],a^=tmp[j];
else { ok=0; tA.base[j]=u; tmp[j]=a; break; }
}
if(ok) C.insert(a);
}
return C;
}
};
本文作者:CharlieVinnie
本文链接:https://www.cnblogs.com/Charlie-Vinnie/p/17084753.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2022-02-02 线段树历史区间最值小记🐤