线性基总结
[模板]线性基
luogu
给定n个整数(数字可能重复),求在这些数中选取任意个,使得他们的异或和最大。
\(1\le n \le 50,0\le S_i \le 2^{50}\)
线性基
线性基其实就是构造出一组序列\(p_0,p_1...p_n\),使得从这些数中任选一个子集的异或和的值域同等与从原序列中任选一个子集的异或和的值域。
同时保证:\(p_i\)在二进制下的最高位是\(2^i\)。
这样在求异或和最大的时候就可以按位贪心了。
构造方法:对于一个需要插入线性基的数\(x\),首先找到它在二进制下的最高位\(i\),检查\(p_i\)是否已经生成。若尚未生成则令\(p_i=x\)并结束操作,否则令\(x=x\ \rm xor\ p_i\),并继续插入操作。
放一段线性基的代码:
struct xxj{
ll p[70];
void insert(ll x)
{
for (int j=63;j>=0;--j)
{
if (!(x>>j)) continue;
if (!p[j]) {p[j]=x;return;}
x^=p[j];
}
}
ll query(ll x)
{
for (int j=63;j>=0;--j)
x=max(x,x^p[j]);
return x;
}
}S;
其中\(\rm query(x)\)是查询异或上\(\rm x\)后的最大值,如果要直接查最大值的话直接\(\rm query(0)\)就好了。
线性基的合并
两个线性基是可以合并的。只要把其中一个暴力插入到另一个里面就行了。
xxj merge(xxj a,xxj b)
{
for (int j=63;j>=0;--j) if (b.p[j]) a.insert(b.p[j]);
return a;
}
合并的复杂度是\(O(\log^2n)\)的。
线性基查询第k小
查询线性基中能够构造出的数字的第k小。
为了查第k小我们需要重构一下原有的线性基。保证:线性基中某一个元素的非最高位为1当且仅当这个位置对应的线性基元素不存在。
要这样重构其实也很简单,对于每个元素从最高位的下一位开始枚举,尽可能地除去非最高位的1。这时候如果有1而不能除去就当且仅当这个位置对应的线性基元素不存在(为0)。
这样子就保证了从高位往地位贪心去做,异或高位一定优于不异或,所以可以用经典的第k小问题的方法去解决。
贴一段代码。
void rebuild()
{
for (int i=62;~i;--i)
for (int j=i-1;~j;--j)
if (p[i]&(1ll<<j)) p[i]^=p[j];
for (int i=0;i<=62;++i) if (p[i]) q[cnt++]=p[i];
}
[HDU3949]Xor
第k小模板题。注意要特判一下给出的这些数能不能异或出一个零。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
ll gi()
{
ll x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
struct xxj{
ll p[65],q[65];int fg,cnt;
void init(){memset(p,0,sizeof(p));fg=cnt=0;}
void insert(ll x)
{
for (int i=62;~i;--i)
{
if (!(x>>i)) continue;
if (!p[i]) {p[i]=x;return;}
x^=p[i];
}
fg=1;
}
void rebuild()
{
for (int i=62;~i;--i)
for (int j=i-1;~j;--j)
if (p[i]&(1ll<<j)) p[i]^=p[j];
for (int i=0;i<=62;++i) if (p[i]) q[cnt++]=p[i];
}
ll query(ll k)
{
if (fg) --k;
if (k>=(1ll<<cnt)) return -1;
ll res=0;
for (int i=cnt-1;~i;--i)
if (k&(1ll<<i)) res^=q[i];
return res;
}
}S;
int main()
{
int T=gi();
for (int zsy=1;zsy<=T;++zsy)
{
int n=gi();S.init();
while (n--) S.insert(gi());
S.rebuild();
int q=gi();printf("Case #%d:\n",zsy);
while (q--) printf("%lld\n",S.query(gi()));
}
return 0;
}