求线性基模板
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 1e5 + 10;
int maxbit = 64;
ll a[N],p[N]; //p数组储存所有的线性基
int n;
int main(){
scanf("%d",&n);
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
for(int j=maxbit-1;j>=0;j--){
if((a[i] >> j) & 1){
if(p[j] == 0){
p[j] = a[i];
break;
}else a[i] ^= p[j];
}
}
}
int r = 0;
for(int i=maxbit-1;i>=0;i--) /
if(p[i]) r++;
cout<<r<<endl; //线性基的个数
ll k;
while(cin>>k){
if(k == 0){
if(r == n) cout<<"False"<<endl;
else cout<<"True"<<endl;
}else{
bool flag = false;
for(int j=maxbit-1;j>=0;j--){
if((k>>j) & 1) k ^= p[j];
if(k == 0){
flag = true;
break;
}
}
if(flag) cout<<"True"<<endl;
else cout<<"False"<<endl;
}
}
return 0;
}
luogu3812
题意:求最大能抑或的数
题解:从高位到地位扫描,每次保存最大的数。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 50 + 10;
int const maxbit = 64;
ll a[N],p[maxbit]; //p数组储存所有的线性基
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++){
for(int j=maxbit-1;j>=0;j--){
if((a[i] >> j) & 1){
if(p[j] == 0){
p[j] = a[i];
break;
}else{
a[i] ^= p[j];
}
}
}
}
ll ans = 0;
for(int i=maxbit-1;i>=0;i--){
if(p[i]) ans = max(ans,ans ^ p[i]);
}
cout<<ans<<endl;
return 0;
}
HDU3949
题意:求n个数能表示的第k小的数是多少。
题解:找到线性基,化简为最简式。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 10000 + 10;
int const maxbit = 64;
ll a[N],p[N],l[N];
int n,Q;
int main(){
int T,caser = 0;
scanf("%d",&T);
while(T--){
printf("Case #%d:\n",++caser);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
scanf("%d",&Q);
memset(p,0,sizeof(p));
for(int i=1;i<=n;i++){
for(int j=maxbit-1;j>=0;j--){
if((a[i] >> j) & 1){
if(p[j] == 0){
p[j] = a[i];
break;
}else{
a[i] ^= p[j];
}
}
}
}
for(int i=maxbit-1;i>=0;i--){ //转化为最简式,每一列只有一个1
for(int j=i+1;j<maxbit;j++){
if((p[j] >> i) & 1){
p[j] ^= p[i];
}
}
}
int r = 0;
for(int i=0;i<maxbit;i++){
if(p[i]){
l[r++] = p[i];
}
}
while(Q--){
ll k;
scanf("%lld",&k);
if(r != n) k--; //0可以表示
if(k >= 1LL << r){
printf("-1\n");
}else{
ll ans = 0;
for(int j=0;j<maxbit;j++){
if((k >> j) & 1)
ans ^= l[j];
}
printf("%lld\n",ans);
}
}
}
return 0;
}
BZOJ2844
题意:对所有子集的抑或结果排序,查找给定的数的排名。
题解:设基的大小为r,那么每个数出现的次数为2^(n-r)。对于给定的数x,计算比x大的数有多。
#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
int const maxbit = 64;
int const mod = 10086;
int n;
int a[N],p[maxbit],l[maxbit],Q;
int qpow(int a,int n){
int ans = 1;
while(n){
if(n&1) ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=maxbit-1;j>=0;j--){
if((a[i] >> j) & 1){
if(p[j] == 0){
p[j] = a[i];
break;
}else{
a[i] ^= p[j];
}
}
}
}
scanf("%d",&Q);
int r = 0;
int ans = 0;
for(int i=0;i<maxbit;i++){
if(p[i])
l[i] = r++;
}
for(int i=0;i<maxbit;i++){
if(p[i] && (Q >> i) & 1){
ans += (1<<l[i]);
}
}
printf("%d\n",(ans % mod * qpow(2,n-r) % mod + 1) % mod);
return 0;
}
BZOJ2460
题意:选取的能量值和最大,但是序号的XOR值不能为0。
题解:贪心策略,选取能量值最大的。如果后面选取的和前面的抑或和为0,那么不选取。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 1000 + 10;
int const maxbit = 64;
struct Node
{
ll val,id;
bool operator < (const Node& e)const{
return val > e.val;
}
}a[N];
int n;
ll p[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].id,&a[i].val);
}
sort(a+1,a+1+n);
ll ans = 0;
for(int i=1;i<=n;i++){
for(int j=maxbit-1;j>=0;j--){
if((a[i].id >> j) & 1){
if(p[j] == 0){
p[j] = a[i].id;
break;
}else{
a[i].id ^= p[j];
}
}
}
if(a[i].id)
ans += a[i].val;
}
printf("%lld\n",ans);
return 0;
}
BZOJ2115
题意:找出从1到nXOR和最大的路劲。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const maxbit = 64;
int const N = 50000 + 10;
int const M = 100000 + 10;
struct Edge
{
int to;
ll val;
Edge(int _to,ll _val):to(_to),val(_val){};
};
vector<Edge>G[N];
int n,m,cnt;
bool vis[N];
ll xorc[N<<1],p[N]; //环的个数可能大于点的个数
ll xorsum[N]; //记录从1到每一个点的XOR和
void dfs(int u,ll val){ //val表示从1到nowp的XOR和
vis[u] = true;
xorsum[u] = val;
for(int i=0;i<G[u].size();i++){
Edge &e = G[u][i];
if(vis[e.to]){ //如果这个点之前来过,说明存在环,记录环的大小XOR和
xorc[++cnt] = val ^ e.val ^ xorsum[e.to];;
}else{
xorsum[e.to] = val ^ e.val;
dfs(e.to,xorsum[e.to]);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
ll val;
scanf("%d%d%lld",&u,&v,&val);
G[u].push_back(Edge(v,val));
}
dfs(1,0);
for(int i=1;i<=cnt;i++){ //找出线性无关的基
for(int j=maxbit-1;j>=0;j--){
if((xorc[i] >> j) & 1){
if(p[j] == 0){
p[j] = xorc[i];
break;
}else{
xorc[i] ^= p[j];
}
}
}
}
ll ans = xorsum[n];
for(int i=maxbit-1;i>=0;i--){
if(p[i])
ans = max(ans,ans ^ p[i]);
}
printf("%lld\n",ans);
return 0;
}
CF448C
题意:有多少个集合,他们的乘积是完全平方数
题解:每个素数表示二进制的一位。完全平方数满足每个质数出现的次数为1,所以XOR后的结果为0,那么就满足完全平方数。所以结果为2^(n-r) - 1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 1e5 + 10;
int const maxbit = 20;
int const mod = 1e9 + 7;
int const prime[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67};
int a[N],p[maxbit];
int n;
void factor(int n,int id){
for(int i=0;i<19;i++){
int now = 0;
while(n % prime[i] == 0){
now ^= 1;
n /= prime[i];
}
a[id] |= now * (1<<i);
}
}
ll qpow(ll a,ll n){
ll ans = 1;
while(n){
if(n&1) ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
factor(x,i);
}
int r = 0;
for(int i=1;i<=n;i++){
for(int j=maxbit-1;j>=0;j--){
if((a[i] >> j) & 1){
if(p[j] == 0){
p[j] = a[i];
r++;
break;
}else{
a[i] ^= p[j];
}
}
}
}
printf("%lld\n",(qpow(1ll*2,n-r) - 1 + mod) % mod);
return 0;
}
CF1100F
题意:对于每一次询问,求区间最大抑或值
题解:离线查找。记录以i为右端点的所有询问。贪心的寻找基,如果找的一个基可以取代旧的基,其坐标比原先的大,那么就选择,交换选择现在的基。因为越靠近右端点,它能被利用的区间越多。
代码:
#include <bits/stdc++.h>
using namespace std;
int const N = 500000 + 10;
int const maxbit = 20;
int n,Q,c[N],l[N],r[maxbit],p[maxbit],ans[N];
vector<int>q[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
scanf("%d",&Q);
for(int i=1;i<=Q;i++){
int r;
scanf("%d%d",&l[i],&r);
q[r].push_back(i);
}
for(int i=1;i<=n;i++){
int x = c[i];
int k = i;
for(int j=maxbit-1;j>=0;j--){
if((x >> j) & 1){
if(!r[j]){
r[j] = x;
p[j] = k;
break;
}else if(p[j] < k){
swap(r[j],x);
swap(p[j],k);
}
x ^= r[j];
}
}
for(int j=0;j<q[i].size();j++){
int t = q[i][j];
for(int k=maxbit-1;k>=0;k--) if(p[k] >= l[t]){
ans[t] = max(ans[t],ans[t] ^ r[k]);
}
}
}
for(int i=1;i<=Q;i++)
printf("%d\n",ans[i]);
return 0;
}
牛客2019多校第四场B
题意:给定n个线性基,判断给定一个区间的所有线性基是否都能够产生x。
题解:线性交模板
代码
#include <bits/stdc++.h>
using namespace std;
#define lson id<<1
#define rson id<<1|1
typedef long long ll;
int const maxbit = 32;
int const N = 50000 + 10;
int const M = 40;
ll a[N][M],x;
int n,m;
struct Base
{
ll r[maxbit],f[maxbit];
void init(){
for(int i=0;i<maxbit;i++)
r[i] = f[i] = 0;
}
bool insert(ll x){
for(int i=maxbit-1;i>=0;i--) if(x >> i){
if(!r[i]){ r[i] = x; return true;}
x ^= r[i];
if(!x) return false;
}
return false;
}
void insertn(ll x){ //插入新的基
ll tmp = x;
for(int i=maxbit-1;i>=0;i--) if(x >> i){
if(!r[i]){ f[i] = tmp; r[i] = x; return;}
x ^= r[i];tmp ^= f[i];
if(!x) return;
}
return;
}
bool find(ll x){
for(int i=maxbit-1;i>=0;i--) if(x >> i){
if(!r[i]) return false;
x ^= r[i];
}
return x == 0;
}
ll cacl(ll x){ //找到x是由基B中的哪几个基组成的
ll ret = 0;
for(int i=maxbit-1;i>=0;i--) if(x >> i){
ret ^= f[i]; //x是由新基种类的
x ^= r[i];
}
return ret;
}
};
struct Node
{
int l,r;
Base base;
}node[N<<2];
void push_up(int id){
Base base = node[lson].base;
Base ans;
ans.init();
for(int i=31;i>=0;i--){
ll x = node[rson].base.r[i];
if(base.find(x)) ans.insert(x ^ base.cacl(x)); //如果在A能够表示,消除B的影响插入
else base.insertn(x); //如果A不能够表示,直接插入
}
node[id].base = ans;
}
void build(int id,int l,int r){
node[id].l = l, node[id].r = r;
if(l == r){
for(int i=0;i<=31;i++)
node[id].base.insert(a[l][i]);
}else{
int mid = (l + r) >> 1;
build(lson,l,mid);
build(rson,mid+1,r);
push_up(id);
}
}
bool Query(int id,int l,int r,ll x){
if(node[id].l == l && node[id].r == r) {
return node[id].base.find(x);
}
int mid = (node[id].l + node[id].r) >> 1;
if(r <= mid) return Query(lson, l, r, x);
else if(l > mid) return Query(rson, l, r, x);
else {
return Query(lson, l, mid, x) && Query(rson, mid + 1, r, x);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int sz;
scanf("%d",&sz);
for(int j=0;j<sz;j++) scanf("%lld",&a[i][j]); //表示第i组数的第j个基
for(int j=sz;j<=31;j++) a[i][j] = 0;
}
build(1,1,n);
while(m--){
int l,r;
scanf("%d%d%lld",&l,&r,&x);
if(Query(1,l,r,x)) printf("YES\n");
else printf("NO\n");
}
return 0;
}