做法?
开题时没思路,想着打表看看能不能找规律:哪些区间异或起来能得到 $0$?于是有了以下代码:
:::info[打表代码]
#include <bits/stdc++.h>
#define ll long long
#define str string
#define db double
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
// 枚举右端点
for (ll i = 1; i <= 30; ++i) {
ll cnt = i;
cout << i << ": ";
// 枚举左端点
for (ll j = i - 1; j >= 1; j--) {
cnt ^= j;
if(cnt == 0) cout << j << ' ';
}
cout << '\n';
}
return 0;
}
:::
然后你就会获得这样一份结果:
找到规律了吗?
我们将左端点为 $1$ 的区间视作左端点为 $0$,那么就得出一下结论:
- 除了 $1$,所有奇数都可以作为右端点,偶数都不行。
- 对于模 $4$ 等于 $3$ 的右端点,其左端点有 $0,4,8,\dots,4 \times n$;对于模 $4$ 等于 $1$ 的右端点,其左端点有 $2,6,10,\dots,4 \times n + 2$。
既然要求这种区间必须包含值 $x$,那么求出 $x$ 左侧的左端点,右侧的右端点,再相乘即可。
具体的计算逻辑应该没必要讲,需要注意的是无论是左端点还是右端点都是 可以 包含值 $x$ 的,不要少算了。
好吧还是证明一下,但是和其他题解的证明差不多。
令 $x = 4 \times n$,那么对于任意 $n \in N^+$,有 $x \oplus (x+1) \oplus (x+2) \oplus (x+3) = 0$
因为这 $4$ 个数中,除去最低的两个二进制位,其余位置完全相等,异或起来结果一定为 $0$,而最低的两个位置上也都分别只出现了 $2$ 个 $1$,异或起来也是 $0$,所以从 $0$ 开始每 $4$ 个连续的整数异或结果都为 $0$。
令 $f(i) = 0 \oplus 1 \oplus 2 \oplus 3 \oplus \dots \oplus i$。
当 $i \bmod 4 = 0$ 时:
$$ \begin{aligned} f(i) &= (0 \oplus 1 \oplus 2 \oplus 3) \oplus (4 \oplus 5 \oplus 6 \oplus 7) \oplus \dots \oplus i\\ &= 0 \oplus 1 \oplus \dots \oplus i\\ &= i \end{aligned} $$当 $i \bmod 4 = 1$ 时:
$$ \begin{aligned} f(i) &= f(i-1) \oplus i\\ &= (i-1) \oplus i\\ &= 1 \end{aligned} $$(注:因为 $i \equiv 1 \pmod 4$ 所以 $i$ 的最低两位是 01,$i-1$ 的最低两位是 00 所以除了最低位其余位全是 0)
当 $i \bmod 4 = 2$ 时:
$$ \begin{aligned} f(i) &= f(i-1) \oplus i\\ &= 1 \oplus i\\ &= i+1 \end{aligned} $$
(注:因为 $i \equiv 2 \pmod 4$ 所以 $i$ 的最低两位是 10 所以 $1 \oplus i$ 的最低两位是 11,所以结果也就是 $i+1$)
当 $i \bmod 4 = 3$ 时
$$ \begin{aligned} f(i) &= f(i-1) \oplus i\\ &= (i-1+1) \oplus i\\ &= 0 \end{aligned} $$就这样吧,剩下的推导很明显了,便不再细讲。
Code
#include <bits/stdc++.h>
#define ll long long
#define str string
#define db double
using namespace std;
constexpr ll mod = 998244353;
ll t, n, x;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin >> t;
while (t--) {
cin >> n >> x;
// mod 4 = 3 的右端点
ll r1 = ((n + 1) / 4 - x / 4) % mod;
// mod 4 = 1 的右端点
ll r2 = ((n - 1) / 4 - (x - 2) / 4) % mod;
// 从 0(实际为1) 开始的左端点
ll l1 = ((x + 4) / 4) % mod;
// 从 2 开始的左端点
ll l2 = ((x + 2) / 4) % mod;
// 相乘即可
cout << (l1 * r1 % mod + l2 * r2 % mod) % mod << '\n';
}
return 0;
}

说些什么吧!