学习线性基可考虑以下大佬博客
知乎Pecco博客
博客园Kaori博客
menci博客
肖然博客
从线性代数谈线性基(有点硬核)
构造线性基
普通插入:
不能保证除了主元上其他线性基元素该位置为1
typedef unsigned long long ull;
ull p[64];
void insert(ull x)
{
for(int i=63;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(!p[i])
{
p[i]=x;
break;
}
x^=p[i];
}
}
进阶插入:
若p[i]!=0(即主元i存在),则线性基中只有p[i]的第i位是1;且此时p[i]的最高位就是第i位
bool insert(ll x)
{
for(int i=62;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(p[i])
{
x^=p[i];
continue;
}
for(int j=i-1;j>=0;j--)
if(x>>j&1) x^=p[j];
for(int j=62;j>i;j--)
if(p[j]>>i&1) p[j]^=x;
p[i]=x;
return 1;
}
return 0;
}
线性基模板操作
1.在一系列数中选若干数做异或,求最大值。
反向遍历p[]
数组,按高位贪心(因为对于每个p[i]
来说,当且仅当目前的res的第i
位为0时,选这个数可以使res增大。而此时,选它一定比不选它更优)
普通插入代码:
ull getmax()
{
ull res=0;
for(int i=63;i>=0;i--)
if((res^p[i])>res) res^=p[i];
return res;
}
进阶插入:直接把所有p[i]异或即是最大值。
2.在一系列数中选若干数做异或,求最小值。
直接求线性基,然后选最小的一个即可。显然它与线性基中任意其他数异或后,都不会更小。
注意:特判0
普通插入代码:
ull getmin()
{
if(cnt0) return 0;
for(int i=0;i<=63;i++)
if(p[i])
return p[i];
}
进阶插入:找到最小主元即可
3.查询某数是否能被表示出来
按照插入的思路进行检查即可。
bool belong(ull x)
{
for(int i=63;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(!p[i]) return false;
x^=p[i];
}
return true;
}
4.查询第k小值
使用进阶插入解决此问题,把k进行二进制分解,把1对应位置的主元xor起来。
注意:第0小是0
ll now[64];
int cnt=0;
for(int i=0;i<=62;i++)
if(p[i]) now[cnt++]=p[i];
ll query(ll k)
{
if(flag) k--;
if(!k) return 0;
if(k>=(1ull<<cnt)) return -1;
ll res=0;
for(int i=0;i<cnt;i++)
if(k>>i&1) res^=now[i];
return res;
}
线性基相关题目
【模板】线性基
普通插入即可解决
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=70;
ull p[N];
int n;
void insert(ull x)
{
for(int i=63;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(!p[i])
{
p[i]=x;
break;
}
x^=p[i];
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
ull x;
cin>>x;
insert(x);
}
ull res=0;
for(int i=63;i>=0;i--)
if((res^p[i])>res) res^=p[i];//不难发现只有(res>>i&1)==0是才会满足if语句
cout<<res<<'\n';
return 0;
}
异或运算
第k异或小,使用进阶插入即可解决。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=10010;
ll p[70];
int n,q;
bool flag;
ll now[70];
int cnt;
bool insert(ll x)
{
for(int i=62;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(p[i])
{
x^=p[i];
continue;
}
for(int j=i-1;j>=0;j--)
if(x>>j&1) x^=p[j];
for(int j=62;j>i;j--)
if(p[j]>>i&1) p[j]^=x;
p[i]=x;
return 1;
}
return 0;
}
ll query(ll k)
{
if(flag) k--;
if(!k) return 0;
if(k>=(1ull<<cnt)) return -1;
ll res=0;
for(int i=0;i<cnt;i++)
if(k>>i&1) res^=now[i];
return res;
}
int main()
{
int T;
cin>>T;
for(int ca=1;ca<=T;ca++)
{
printf("Case #%d:\n",ca);
memset(p,0,sizeof p);
flag=0;cnt=0;
cin>>n;
for(int i=1;i<=n;i++)
{
ll x;
cin>>x;
if(!insert(x)) flag=1;
}
cin>>q;
for(int i=0;i<=62;i++)
if(p[i]) now[cnt++]=p[i];
while(q--)
{
ll k;
cin>>k;
cout<<query(k)<<'\n';
}
}
return 0;
}
[BJWC2011]元素
按照magic逆序,然后线性基插入
#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
const int N=1010;
ull p[N];
struct node
{
ll id,w;
bool operator<(const node& o) const
{
return w>o.w;
}
}q[N];
int n;
bool insert(ull x)
{
for(int i=63;i>=0;i--)
{
if(!(x>>i&1)) continue;
if(!p[i]) return p[i]=x,1;
x^=p[i];
}
return 0;
}
int main()
{
IO;
int T=1;
//cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>q[i].id>>q[i].w;
sort(q+1,q+1+n);
ll res=0;
for(int i=1;i<=n;i++)
{
if(insert(q[i].id))
res+=q[i].w;
}
cout<<res<<'\n';
}
return 0;
}
Good subset
线性基+线段树题解
搜索题解
2020/09/30 要加油哦~