题意:
一共有n个集合,m个询问,每次询问在[l,r]之间的所有集合是否能异或出x来,能输出YES,否则输出NO
大部分博客都只讲了线性基的并,没有讲线性基求交
给不小心进来的朋友安利一篇超超超清楚详细的线性基求交
(好像这个博客也是转载的)
/*
思路:由于本题是求[l,r]区间内的集合是否都能异或出x来,故考虑使用线段树来维护[l,r]之间的线性基的交集
线性基求交:
设A,B分别是空间V1,V2的一组基,C是A,B的交集
如果Bi可以由A[1,n]和B[1,i-1]中的某些维异或得到,即Bi=Aa1^Aa2^Aa3...^Bb1^Bb2...
则Aa1^Aa2^Aa3...^Bb1^Bb2...^Bi=0,故num=(Aa1^Aa2^Aa3...)=(Bb1^Bb2...^Bi),故这个数是A,B两组基都能异或出来的数
故num=Aa1^Aa2^Aa3...就可以插入答案C中
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4+10;
typedef struct {
ll d[35];
void insert(ll val) {
for (int i = 32; i >= 0; i--) {
if (val & (1LL << i)) {
if (!d[i]) {
d[i] = val;
break;
}
val ^= d[i];
}
}
}
}Linear_Basis;
typedef struct {
int l, r;
Linear_Basis lb;
}SegmentTree;
SegmentTree st[4 * N];
Linear_Basis LB[N];
bool query_exist(ll x, Linear_Basis lb) {
for (int i = 32; i >= 0; i--) {
if (x & (1LL << i)) {
if (!lb.d[i])
break;
x ^= lb.d[i];
}
}
return x == 0;
}
Linear_Basis Union(const Linear_Basis& A, const Linear_Basis& B) { //线性基求交
Linear_Basis All, C, D; //All用来维护可能可以组成Bi的集合,C为要返回的答案,D是记录All中的每一位都是由A中的哪些位异或出来的
All = A;
memset(C.d, 0, sizeof(C.d));
for (int i = 32; i >= 0; i--)D.d[i] = 1ll << i; //一开始初始化ALL=A,故Di就是A的第i位得来的
for (int i = 32; i >= 0; i--) {
if (!B.d[i])continue; //B的第i维不存在,不用对C进行插入操作
//B的第i维存在
bool can = true;
ll v = B.d[i], k = 0; //k用来记录用了A中的哪些元素
for (int j = 32; j >= 0; j--) {
if (!(v >> j))continue;
if (!All.d[j]) { //当前B[i]不能由All中的元素异或得到,则插入到All中
can = false;
All.d[j] = v;
D.d[j] = k;
break;
}
else {
v ^= All.d[j];
k ^= D.d[j];
}
}
if (can) { //Bi可以由All中的元素得到
for (int j = 32; j >= 0; j--)
if (k & (1ll << j))v ^= A.d[j]; //将Aa1^Aa2^...加入答案
C.insert(v);
}
}
return C;
}
void build(int roo,int l,int r) { //建立线段树
st[roo].l = l;
st[roo].r = r;
if (l == r) {
st[roo].lb = LB[l];
return;
}
int m = (l + r) / 2;
build(roo*2,l, m);
build(roo * 2 + 1,m + 1, r);
st[roo].lb = Union(st[roo * 2].lb, st[roo * 2 + 1].lb);
}
bool query(int roo, int L, int R,ll x) { //查询
if (st[roo].l >= L && st[roo].r <= R)
return query_exist(x, st[roo].lb);
int m = (st[roo].l + st[roo].r) / 2;
bool ans=true;
if (L <= m)ans = query(roo * 2, L, R, x);
if (R > m)ans = (ans & query(roo * 2 + 1, L, R, x));
return ans;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
int cnt;
ll x;
scanf("%d", &cnt);
while (cnt--) {
scanf("%lld", &x);
LB[i].insert(x);
}
}
build(1, 1, n);
while (m--)
{
int l, r;
ll x;
scanf("%d%d%lld", &l, &r, &x);
bool ans = query(1, l, r, x);
printf(ans ? "YES\n" : "NO\n");
}
return 0;
}