ACM模板
文章目录
- 构造线性基
- 线性基模板操作
- 线性基相关题目
学习线性基可考虑以下大佬博客
知乎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 要加油哦~