题目
求
n
n
n个数,每个数在
[
L
,
R
]
[L,R]
[L,R]间,异或和为
K
K
K的方案数。
考虑
N
N
N个数
a
i
a_i
ai每个数在
[
0
,
R
]
[0,R]
[0,R]之间的问题。
发现如果枚举一个
i
i
i表示对于
∀
j
∈
[
1
,
N
]
,
k
∈
[
i
+
1
,
29
]
\forall j\in [1,N],k\in[i+1,29]
∀j∈[1,N],k∈[i+1,29]有
a
j
>
>
k
&
1
=
R
>
>
k
&
1
a_{j}>>k\&1 = R>>k\&1
aj>>k&1=R>>k&1即所有数在
i
i
i位以上都和上界相等,并且在
i
i
i位存在一个
a
i
a_i
ai和上界
R
R
R不相等,那么剩下的
i
−
1
i-1
i−1位就有了一个可以任意支配的数,我们可以把其他数的后
i
−
1
i-1
i−1位填了,然后用这个数的后
i
−
1
i-1
i−1位凑成
K
K
K的后
i
−
1
i-1
i−1位。
那么现在就只需要前
[
i
,
29
]
[i,29]
[i,29]位的异或值分别对位相等即可。
[
i
+
1
,
29
]
[i+1,29]
[i+1,29]位的异或值是固定的,直接判断即可。
第
i
i
i位的异或值就相当于一个简单
d
p
dp
dp,可以用矩阵快速幂优化。
具体是
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示前
i
i
i个数,异或值为
j
j
j,并且
k
=
1
k=1
k=1则存在一个
a
p
∈
[
1
,
i
]
a_{p\in[1,i]}
ap∈[1,i]在这一位和上界不相等。
把后两维看作矩阵就可以有理有据的矩阵乘法优化了。
实际上
R
R
R不一定对于每一个数都必须相等,只是这时就不是矩阵快速幂而是线段树维护矩阵积写动态
d
p
dp
dp了。
拓展到
[
L
,
R
]
[L,R]
[L,R]也很简单。
就是每次选上界可以是
R
R
R也可以是
L
−
1
L-1
L−1,但是选
L
−
1
L-1
L−1的时候需要乘上一个
−
1
-1
−1的容斥系数。
注意到这时
[
i
+
1
,
29
]
[i+1,29]
[i+1,29]位的异或值不一定是固定的,但是我们只需要额外记录一下选出
R
R
R个数的奇偶性就可以判断出选出
L
L
L个数的奇偶性然后判断
[
i
+
1
,
29
]
[i+1,29]
[i+1,29]位的异或值了。
所以最后转移矩阵是
8
×
8
8 \times 8
8×8的。
注意复杂度不要带
Q
Q
Q,稍微预处理一下。
A C C o d e \mathcal AC \ Code AC Code
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
#define mod 1000000007
using namespace std;
struct mat{
int a[8][8];
mat(int d=0){memset(a,0,sizeof a);rep(i,0,7) a[i][i] = d;}
mat operator *(const mat &B)const{
mat r;
rep(i,0,7) rep(j,0,7) rep(k,0,7)
r.a[i][k] = (r.a[i][k] + 1ll * a[i][j] * B.a[j][k]) % mod;
return r;
}
}A[30];
mat Pow(mat b,int K){
mat r(1);
for(;K;K>>=1,b=b*b) if(K&1) r=r*b;
return r;
}
int n,L,R,Q,K,C[2];
int main(){
int T;
for(scanf("%d",&T);T--;){
scanf("%d%d%d%d",&n,&C[0],&C[1],&Q);
C[0] --;
per(i,29,0) {
memset(A[i].a,0,sizeof A[i].a);
rep(a,0,1) rep(b,0,1) rep(c,0,1) rep(d,0,1) if(C[d] >= 0) rep(e,0,C[d]>>i&1){
int x = a ^ e , y = b | (e < (C[d] >> i & 1)) , z = c ^ d;
if(!b && y)
A[i].a[a * 4 + b * 2 + c][x * 4 + y * 2 + z] += (d == 1 ? 1 : -1);
else if(e < (C[d] >> i & 1))
A[i].a[a * 4 + b * 2 + c][x * 4 + y * 2 + z] += (d == 1 ? 1 : -1) << i;
else
A[i].a[a * 4 + b * 2 + c][x * 4 + y * 2 + z] += (d == 1 ? 1 : -1) * ((C[d] & ((1 << i) - 1)) + 1);
}
A[i] = Pow(A[i],n);
}
for(;Q--;){
scanf("%d",&K);
int ans = 0;
per(i,29,0){
rep(a,0,1) rep(c,0,1) if(((c * C[1]) ^ ((n - c & 1) * C[0]) ^ K) >> i+1 == 0 && a == (K >> i & 1)){
ans = (ans + A[i].a[0][a * 4 + 1 * 2 + c]) % mod;
}
if(i == 0){
rep(a,0,1) rep(c,0,1) if(((c * C[1]) ^ ((n - c & 1) * C[0]) ^ K) == 0)
ans = (ans + A[i].a[0][a * 4 + c]) % mod;
}
}
printf("%d\n",(ans+mod)%mod);
}
}
}