题面
Mahmoud and Ehab and yet another xor task
题意:
⋅
\cdot
⋅ 给定一个
n
n
n 个数的序列
{
a
i
}
\{ a_i \}
{ai}。
⋅
\cdot
⋅ 有
q
q
q 次形如
(
l
,
x
)
(l,x)
(l,x) 的询问,每次询问要求输出前
l
l
l 个数中,有多少子序列满足异或和等于
x
x
x。
⋅
\cdot
⋅
1
≤
n
,
q
≤
1
0
5
,
0
≤
a
i
≤
2
20
1 \leq n,q \leq 10^5,0 \leq a_i \leq 2^{20}
1≤n,q≤105,0≤ai≤220。
分析:
解决这类 从一堆数中选出若干个,使得它们的异或和为某一个数,问有多少种选法 的题,有一种固定的思路:
构建这堆数的 线性基,然后把这堆数分成两类:在线性基中的数 和 不在线性基中的数。 设第二类数的数量为 n n n,如果线性基能够异或出来数 x x x,那么答案就是 2 n 2^n 2n。否则答案为 0 0 0。
我们考虑证明一下: 首先我们需要明确,对于一个线性基而言,如果它能异或出来一个数,那么选取方案 有且只有一个。 我们考虑对于这堆数的第二类数,它们所有能异或出来的数字,线性基都可以唯一的异或出来。 如果线性基能够唯一异或出来 x x x,那么我们在非线性基的数中任选,设这些数的异或值为 y y y,那么线性基只要能异或出来 x ⊕ y x \oplus y x⊕y,那么就可以构出一种方案。因为 线性基能选一些数异或出来 x x x,又能选一些数异或出来 y y y,我们将这两拨数异或就得到了 x ⊕ y x \oplus y x⊕y。所以第二堆数都可以 选或不选,所有的方案就是 2 n 2^n 2n。
为什么 当线性基异或不出来 x x x 时,答案为 0 0 0 呢?因为 线性基 和 原序列能异或得到的数字集合 是 一模一样 的,如果线性基都异或不出来,那么原序列也肯定异或不出来。
对于本题而言。因为是多次询问,我们将询问离线,使 l l l 从前往后递增,这样每次把新数插入线性基就好了。复杂度 O ( n ∗ 20 ) O(n * 20) O(n∗20)。
CODE:
#include<bits/stdc++.h>// 答案 2^|S|
#define mod 1000000007
using namespace std;// 某一集合异或出某一个数字的方案数的通法
typedef long long LL;
const int N = 1e5 + 10;
int n, a[N], t;
LL ans[N];
namespace B{
int b[30], cnt = 0;
void ins(int x){
for(int i = 20; i >= 0; i--)
if((x >> i) & 1)
if(!b[i]){b[i] = x; cnt++; break;}
else x ^= b[i];
}
bool query(int x){// x能否得到
for(int i = 20; i >= 0; i--){
if((x >> i) & 1){
if(b[i]) x ^= b[i];
else return 0;
}
}
return 1;
}
}
struct Question{
int l, x, id;
}q[N];
bool cmp(Question a, Question b){return a.l < b.l;}
LL Pow(LL x, LL y){
LL res = 1, k = x;
while(y){
if(y & 1) res = (res * k) % mod;
y >>= 1;
k = (k * k) % mod;
}
return res;
}
LL ask(int m){//第 m 个问题
for(int i = q[m - 1].l + 1; i <= q[m].l; i++)
B::ins(a[i]);
if(B::query(q[m].x)) return Pow(2LL, q[m].l - B::cnt);
else return 0;
}
int main(){
scanf("%d%d", &n, &t);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= t; i++){
scanf("%d%d", &q[i].l, &q[i].x);
q[i].id = i;
}
sort(q + 1, q + t + 1, cmp);
for(int i = 1; i <= t; i++){
ans[q[i].id] = ask(i);
}
for(int i = 1; i <= t; i++) printf("%lld\n", ans[i]);
return 0;
}