题意:有n个集合,每个集合有若干元素,一个集合i能表示x,当且仅当存在一个集合i的子集合,这里面的元素异或值为x。
有m个询问:每个为x,l,r,如果任意一个集合i (i在[l,r])都能表示x,输出YES,否则输出NO。
题解:我们给每个集合求一个线性基,如果该集合能表示x,说明x在线性基里面。
题目现在是多询问,而且是多集合,那么也就是要求多个集合的线性基的交,假设x在交里面,输出YES。
这里我们建一个线段树,每个叶子节点就为该集合的线性基,非叶子节点就为左右子节点的线性基的交。
参考牛客已通过的代码,出处找不到了:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=50010;
struct L_B{
int cnt;
LL d[35];
void init(){
memset(d,0,sizeof(d));
}
void Insert(LL x){
for(int i=31;i>=0;i--){
if(x&(1LL<<i)){
if(!d[i]){
d[i]=x;
return ;
}
else x^=d[i];
}
}
}
///检查该值x是否在线性基里面
bool check(LL x){
for(int i=31;i>=0;i--){
if(x&(1LL<<i)){
if(!d[i]) return 0;
else x^=d[i];
}
}
return 1;
}
LL& operator [](int x)
{
return d[x];
}
}t[N*4],tmp,v1;
///求线性基的交
void inter(L_B &x,L_B &y,L_B &ans)
{
tmp=v1=x;
for(int i=0;i<=31;i++)
{
if(y[i])
{
LL x=y[i],now=0;
int f=0;
for(int j=31;j>=0;j--)
{
if(x&(1<<j))
{
if(!tmp[j]){
f=1;
tmp[j]=x;
v1[j]=now;
break;
}
else{
x^=tmp[j];
now^=v1[j];
}
}
}
if(!f) ans.Insert(now);
}
}
}
void build(int k,int l,int r)
{
if(l==r)
{
int sz;
LL x;
scanf("%d",&sz);
///叶子节点,求此集合的线性基
for(int i=1;i<=sz;i++){
scanf("%lld",&x);
t[k].Insert(x);
}
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
inter(t[k<<1],t[k<<1|1],t[k]);///非叶子节点
}
bool query(int k,int l,int r,int L,int R,int x)
{
if(L<=l&&r<=R) return t[k].check(x);
int mid=(l+r)>>1;
if(L<=mid&&!query(k<<1,l,mid,L,R,x)){
return 0;
}
if(R>mid&&!query(k<<1|1,mid+1,r,L,R,x)){
return 0;
}
return 1;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--)
{
int l,r;
LL x;
scanf("%d%d%lld",&l,&r,&x);
if(query(1,1,n,l,r,x)) puts("YES");
else puts("NO");
}
return 0;
}