线性基:处理异或操作的强大工具,思想也是可以借鉴的。
作用:用于处理多个数中选取一些数的XOR的最大值,最小值,第k大值,并可以查询能否通过集合中任意个数XOR得到,时间复杂度为O(n*logn)
具体模板如下,思路和代码均参考于大佬的博客,感谢解惑~
链接:传送门
模板:
struct Linear_Basis
{
int num[max_log];//插入数组
void init()
{
memset(num,0,sizeof(num));
}
//将u插入num数组中
void insert(int u)
{
int i;
for(i=max_log;i>=0;i--)
{
if(u&(1<<i))//如果u的这一位有值,则进行判断
{
if(!num[i])//如果这位为0
{
num[i]=u;//u赋值给它
}
else
{
u^=num[i];//不为0,则u异或这个数
}
}
}
}
/*
操作之后u只有两种结果:
1.被记录到了数组中
2.未被记录,则此时u必定为0,说明此时的线性基已经能通过Xor得到u.
因此我们可以用这种方法来判断此时是否存在Xor值为u的子集.
*/
//查询操作
bool check(int u)
{
int i;
for(i=max_log;i>=0;i--)
{
if(u&(1<<i))//如果u这一位有数
{
if(!num[i])//num[i]这一位为0
{
return 0;
}
else
u^=num[i];//否则u取异或num[i]
}
}
return 1;
}
//查询最大值
int aks_max()
{
int res=0,i;
for(i=max_log;i>=0;i--)
{
if((res^num[i])>res)res^=num[i];
}
return res;
}
//查询最小值
int ask_min()
{
int i;
for(i=0;i<=63;i++)
{
if(num[i]) return num[i];//最小的非0数就是最小值
}
}
//查询第k小/大
/*
首先将数组中的所有数变成包含这个最高位且可以通过Xor得到的最小值.
具体实现方法就是每一位向后扫,若Xor后变小,则Xor.
之后就可以发现第k大/小只要将k改为二进制后,将二进制所对应的位置的数Xor起来即可.
*/
int ask_kth(int k)
{
int i,j,tmp[63],tt=-1,res=0;
for(i=0;i<=63;i++)
{
for(j=i-1;j>=0;j--)
{
if(num[i]&(1 << j)) num[i]^=num[j];
}
if(num[i]) tmp[++tt]=num[i];
}
for(i=0;i<=tt;i++)
{
if((1 << i)&k) res^=tmp[i];
}
return res;
}
};
加个模板题,也是让我知道线性基存在的题:https://www.nowcoder.com/acm/contest/180/D
题目描述:
小a有n个数,他提出了一个很有意思的问题:他想知道对于任意的x, y,能否将x与这n个数中的任意多个数异或任意多次后变为y
思路:注意到若x^一堆东西=y,则必有x^y=一堆东西,就转化成查询集合中任意数异或可不可以达到x^y这个数,也就是线性基模板题了~
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=1e5+10;
const int max_log=100;
int a[maxn];
struct Linear_Basis
{
int num[max_log];//插入数组
void init()
{
memset(num,0,sizeof(num));
}
//将u插入num数组中
void insert(int u)
{
int i;
for(i=max_log;i>=0;i--)
{
if(u&(1<<i))//如果u的这一位有值,则进行判断
{
if(!num[i])//如果这位为0
{
num[i]=u;//u赋值给它
break;
}
else
{
u^=num[i];//不为0,则u异或这个数
}
}
}
}
/*
操作之后u只有两种结果:
1.被记录到了数组中
2.未被记录,则此时u必定为0,说明此时的线性基已经能通过Xor得到u.
因此我们可以用这种方法来判断此时是否存在Xor值为u的子集.
*/
//查询操作
bool check(int u)
{
int i;
for(i=max_log;i>=0;i--)
{
if(u&(1<<i))//如果u这一位有数
{
if(!num[i])//num[i]这一位为0
{
return 0;
}
else
u^=num[i];//否则u取异或num[i]
}
}
if(u==0)return 1;
else return 0;
}
};
Linear_Basis xxj;
int main()
{
int n,m;
scanf("%d",&n);
xxj.init();
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
xxj.insert(a[i]);
}
scanf("%d",&m);
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
int temp=x^y;
if(xxj.check(temp))
{
printf("YES\n");
}
else
{
printf("NO\n");
}
}
return 0;
}