异或(XOR)运算性质
- 定义 :相同为0,不同为1
0 ⊕ 0 = 0 0 ⊕ 1 = 1 1 ⊕ 0 = 1 1 ⊕ 1 = 0
- 具有交换律和结合律
- 自反性 一个数和自己异或得0
a ⊕ a = 0
- 与0异或不变
a ⊕ 0 = a
- 与1异或取反
a ⊕ 1 = ~a(按位取反)
- 可逆性:
a ⊕ b ⊕ b = a
- 奇偶性:在序列异或中非常常用
多个值异或的结果取决于1的个数:- 某一位上1的总数为奇数 → 结果为1
- 某一位上1的总数为偶数 → 结果为0
- 等式性质:
a ⊕ b = c ⇔ a ⊕ c = b ⇔ b ⊕ c = a
- 分配律(与AND结合):
a & (b ⊕ c) = (a & b) ⊕ (a & c)
异或运算实用技巧
- 变量交换(无临时变量)
a ^= b;
b ^= a;
a ^= b;
原理:
a ^= b
→ a = a ⊕ bb ^= a
→ b = b ⊕ (a ⊕ b) = aa ^= b
→ a = (a ⊕ b) ⊕ a = b
- 找缺失数字(数组 0 到 n 中缺一个数)
int findMissingNumber(vector<int>& nums, int n) {
int result = 0;
// 异或所有数字 0 到 n
for (int i = 0; i <= n; i++) {
result ^= i;
}
// 异或数组中的所有数字
for (int num : nums) {
result ^= num;
}
return result;
}
int main() {
vector<int> nums = {0, 1, 3, 4, 5}; // 缺失 2
int n = 5;
cout << "缺失的数字是: " << findMissingNumber(nums, n) << endl;
}
输出:
缺失的数字是: 2
所有数字异或两次会相互抵消,缺失数字只会异或一次
- 找奇数次出现元素(其他元素出现偶数次)
int findOddOccurrence(vector<int>& nums) {
int result = 0;
for (int num : nums) {
result ^= num;
}
return result;
}
int main() {
vector<int> nums = {4, 3, 3, 4, 2, 4, 4, 3, 3, 2, 5};
// 5 出现 1 次(奇数次)
cout << "奇数次出现的元素: " << findOddOccurrence(nums) << endl;
}
输出:
奇数次出现的元素: 5
- 位翻转(使用掩码)
int main() {
unsigned char value = 0b10101010; // 170
unsigned char mask = 0b11110000; // 240
// 翻转 mask 中为 1 的位
unsigned char flipped = value ^ mask;
cout << "原始值: " << (int)value
<< " (二进制: " << bitset<8>(value) << ")" << endl;
cout << "掩码值: " << (int)mask
<< " (二进制: " << bitset<8>(mask) << ")" << endl;
cout << "翻转后: " << (int)flipped
<< " (二进制: " << bitset<8>(flipped) << ")" << endl;
}
输出:
原始值: 170 (二进制: 10101010)
掩码值: 240 (二进制: 11110000)
翻转后: 90 (二进制: 01011010)
- 奇偶校验位计算
bool computeParity(unsigned int data) {
bool parity = false;
while (data) {
parity ^= (data & 1); // 异或最低位
data >>= 1; // 右移一位
}
return parity;
}
int main() {
unsigned int numbers[] = {7, 10, 15, 255};
for (unsigned int num : numbers) {
cout << "数字 " << num << " 的奇偶校验位: "
<< computeParity(num) << endl;
}
}
输出:
数字 7 的奇偶校验位: 1 (二进制 111 → 1的个数为3,奇数)
数字 10 的奇偶校验位: 0 (二进制 1010 → 1的个数为2,偶数)
数字 15 的奇偶校验位: 0 (二进制 1111 → 1的个数为4,偶数)
数字 255 的奇偶校验位: 0 (二进制 11111111 → 1的个数为8,偶数)
- 判断两个数是否异号
#include <iostream>
using namespace std;
bool oppositeSigns(int x, int y) {
return (x ^ y) < 0;
}
int main() {
cout << "10 和 -5 是否异号? " << boolalpha
<< oppositeSigns(10, -5) << endl; // true
cout << "10 和 5 是否异号? " << boolalpha
<< oppositeSigns(10, 5) << endl; // false
cout << "-10 和 -5 是否异号? " << boolalpha
<< oppositeSigns(-10, -5) << endl; // false
}
输出:
10 和 -5 是否异号? true
10 和 5 是否异号? false
-10 和 -5 是否异号? false
原理:
整数最高位是符号位(1 表示负,0 表示正),异号数的符号位不同,异或后符号位为 1(负数)
- 快速检查两个数是否相等
int main() {
int a = 42, b = 42, c = 24;
cout << "a 和 b 相等? " << (a ^ b ? "false" : "true") << endl;
cout << "a 和 c 相等? " << (a ^ c ? "false" : "true") << endl;
return 0;
}
输出:
a 和 b 相等? true
a 和 c 相等? false
比 a == b
更快,因为不需要分支预测
- 加密/解密简单实现
#include <iostream>
#include <string>
using namespace std;
string xorEncryptDecrypt(const string& input, char key) {
string result = input;
for (char& c : result) {
c ^= key;
}
return result;
}
int main() {
string message = "Hello, XOR!";
char key = 'K'; // 加密密钥
string encrypted = xorEncryptDecrypt(message, key);
cout << "加密后: " << encrypted << endl;
string decrypted = xorEncryptDecrypt(encrypted, key);
cout << "解密后: " << decrypted << endl;
return 0;
}
输出:
加密后: +IYYJ
X\SK
解密后: Hello, XOR!
原理:
应用两次异或恢复原始数据:
data ⊕ key ⊕ key = data
- 位操作技巧集合
#include <iostream>
using namespace std;
int main() {
// 1. 设置最低有效位为0
int x = 7; // 0111
x &= (x - 1);
cout << "设置最低位为0: " << x << " (二进制: " << bitset<4>(x) << ")" << endl;
// 2. 检查是否是2的幂
int y = 16;
bool isPowerOfTwo = (y & (y - 1)) == 0;
cout << y << " 是2的幂? " << boolalpha << isPowerOfTwo << endl;
// 3. 交换特定位
unsigned char a = 0b11001100;
unsigned char b = 0b00110011;
unsigned char mask = 0b11110000;
unsigned char swapped = (a & mask) | (b & ~mask);
cout << "交换特定位: " << bitset<8>(swapped) << endl;
return 0;
}
输出:
设置最低位为0: 6 (二进制: 0110)
16 是2的幂? true
交换特定位: 11000011
- 找出只出现一次的数字(其他出现三次)
#include <iostream>
#include <vector>
using namespace std;
int singleNumber(vector<int>& nums) {
int ones = 0, twos = 0;
for (int num : nums) {
ones = (ones ^ num) & ~twos;
twos = (twos ^ num) & ~ones;
}
return ones;
}
int main() {
vector<int> nums = {2, 2, 3, 2, 4, 4, 4}; // 3 出现一次
cout << "只出现一次的数字: " << singleNumber(nums) << endl;
return 0;
}
输出:
只出现一次的数字: 3
例题
来看一道简单题
与或和的异或
题目描述
给定一个长为 n n n 的正整数序列 a 1 , a 2 , … , a n a_1,a_2,…,a_n a1,a2,…,an。
给定两个数
l
,
r
(
1
≤
l
≤
r
≤
n
)
l,r(1≤l≤r≤n)
l,r(1≤l≤r≤n),要求计算
(
a
l
&
a
l
+
1
&
⋯
&
a
r
)
⊕
(
a
l
∣
a
l
+
1
∣
⋯
∣
a
r
)
(a_l \& a_{l+1} \& \cdots \&a_r) ⊕ (a_l|a_{l+1}| \cdots |a_r)
(al&al+1&⋯&ar)⊕(al∣al+1∣⋯∣ar)
其中
&
\&
& ,
∣
|
∣ ,
⊕
⊕
⊕ 分别是按位与、按位或和按位异或运算。
输入格式
第一行输入两个数
n
(
1
≤
n
≤
1
0
5
)
n(1≤n≤10^{5} )
n(1≤n≤105) ,
m
(
1
≤
m
≤
1
0
6
)
m(1≤m≤10^{6})
m(1≤m≤106),表示序列长度和操作数。
第二行,n 个数
a
1
,
a
2
,
…
a
n
a_1,a_2,…a_n
a1,a2,…an(
0
≤
a
i
≤
1
0
9
0≤a_i≤10^9
0≤ai≤109),表示正整数序列。
接下来 m 行,每行 2 个数
l
,
r
(
1
≤
l
≤
r
≤
n
)
l,r (1≤l≤r≤n)
l,r(1≤l≤r≤n)。
输出格式
m 行,每行一个数,表示对应操作的答案。
核心思路 :按位 + 前缀和
- 根据与、或、异或的性质,可以轻松地观察发现,对于子序列(即
a
l
a_l
al 到
a
r
a_r
ar)的二进制第 k 位,当且仅当它们有 0 有 1 时,才对最后的结果有贡献。具体如下:
(1 & 1 & 1) ^ (1 | 1 | 1) = 1 ^ 1 = 0 (1 & 1 & 0) ^ (1 | 1 | 0) = 0 ^ 1 = 1 (0 & 0 & 0) ^ (0 | 0 | 0) = 0 ^ 0 = 0
- 为提高效率,需要采用前缀和的思想。对于整个序列
{
a
n
}
\{a_n\}
{an} ,统计前 i 个数中,第 k 位的 1 的个数,存入
pre[i][k]
. - 当查询
l
l
l 到
r
r
r 的子序列时,可以通过
pre[r][k] - pre[l-1][k]
来获得子序列中所有数的二进制第 k 位的 1 的个数,将其与子序列长度r - l + 1
进行比较,即可知道该位是否有0有1,若是则对总结果有贡献. - 贡献累加:第 k 位若对最终结果有贡献,则其贡献就为 2 k 2^k 2k
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,a[N],pre[N][32];
int read(){
int x=0,f=1; char ch=getchar();
if(ch=='-') f=-1, ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
return x*f;
}
// 计算前缀和pre
void op(int t){
for(int i = 0; i <= 30; i ++){
pre[t][i] = pre[t-1][i];
if((a[t] >> i) & 1 == 1) pre[t][i] ++;
}
}
// 查询
void solve(int l, int r){
int t = r - l + 1;
ll ans = 0;
for(int i = 0; i <= 30; i ++){
if(pre[r][i] - pre[l-1][i] < t && pre[r][i] - pre[l-1][i] > 0)
ans += (1ll << i);
}
printf("%lld\n", ans);
}
int main(){
n = read(); m = read();
for(int i = 1; i <= n; i ++){
a[i] = read();
op(i);
}
for(int i = 1; i <= m; i ++){
int l, r;
l = read(); r = read();
solve(l, r);
}
return 0;
}
看一道异或序列相关的题
P3917异或序列
题目分析
题目要求计算序列
A
1
,
A
2
,
⋯
,
A
N
A_1, A_2, \cdots, A_N
A1,A2,⋯,AN 的所有连续子序列的异或和的总和。即:
∑
1
≤
i
≤
j
≤
N
(
A
i
⊕
A
i
+
1
⊕
⋯
⊕
A
j
)
\sum_{1 \leq i \leq j \leq N} (A_i \oplus A_{i+1} \oplus \cdots \oplus A_j)
1≤i≤j≤N∑(Ai⊕Ai+1⊕⋯⊕Aj)
其中
⊕
\oplus
⊕ 表示按位异或操作。
(
n
≤
1
0
5
,
A
i
≤
1
0
9
)
(n \leq 10^5 , A_i \leq 10^9)
(n≤105,Ai≤109)
核心思路:依旧是按位 + 前缀和
异或操作具有按位独立性,即整个子序列的异或和可以拆分为每个二进制位的独立贡献。因此,我们可以分别计算每个二进制位对答案的贡献,最后将所有位的贡献相加。
∑(所有子序列异或和)
= ∑(每位贡献)
= ∑ₖ[2ⁱ × (第i位为1的子数组数量)]
= ∑ₖ[2ⁱ × ∑ⱼ(val[i][!tⱼ])]
- 前缀异或数组:
- 定义前缀异或数组
pre[]
,其中 p r e 0 = 0 pre_0 = 0 pre0=0, p r e i = A 1 ⊕ A 2 ⊕ ⋯ ⊕ A i pre_i = A_1 \oplus A_2 \oplus \cdots \oplus A_i prei=A1⊕A2⊕⋯⊕Ai ( 1 ≤ i ≤ N ) ( 1 \leq i \leq N ) (1≤i≤N)。 - 根据异或的性质,任意子序列 [ i , j ] [i, j] [i,j] 的异或和可以表示为 p r e j ⊕ p r e i − 1 pre_j \oplus pre_{i-1} prej⊕prei−1。
- 定义前缀异或数组
- 按位贡献计算:
- 对于二进制第 k k k 位(最低位为第 0 位),子序列 [ i , j ] [i, j] [i,j] 在该位的贡献为 1 当且仅当 B j B_j Bj 和 B i − 1 B_{i-1} Bi−1 在第 k k k 位的值不同。
- 因此,问题转化为:对每个位 k k k,统计有多少个子数组在该位为1,即就是统计有多少对 ( l , r ) (l, r) (l,r) 使得 p r e l pre_l prel 和 p r e r pre_r prer 在第 k k k 位不同。
- 贡献累加:
- 维护一个二维数组
cnt[k][0/1]
,记录遍历过程中前缀异或值在第 k k k 位为 0 或 1 的个数。 - 从左到右遍历前缀异或数组
p
r
e
0
,
p
r
e
1
,
⋯
,
p
r
e
N
pre_0, pre_1, \cdots, pre_N
pre0,pre1,⋯,preN:
- 对于当前值 p r e i pre_i prei,检查其第 k k k 位的值 t t t(0 或 1)。
- 能与
p
r
e
i
pre_i
prei形成有贡献子序列的是 所有第
i
位为!t
的历史前缀值 ,即第 k k k 位与 p r e i pre_i prei 相反的所有的 p r e j pre_j prej ( j < i ) (j<i) (j<i),这些子序列(即 [ j + 1 , i ] [j+1,i] [j+1,i])的个数为cnt[k][!t]
,那么它们对最终答案的贡献值就是 个数 ×2^k
=cnt[k][!t] × (1ll<<k)
- 还要记得将当前
p
r
e
i
pre_i
prei 在第
k
k
k 位是 0 还是 1 的计数 加入
cnt[k][t]
。
- 维护一个二维数组
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n, pre[N], cnt[33][2];
ll ans;
void op(int x){
for(int i = 0; i <= 30; i ++){
int t = (x >> i) & 1; // 取x的二进制第i位 t为0或1
ans += (1ll << i) * cnt[i][!t]; // 贡献累加过程
cnt[i][t] ++;
}
}
int main(){
scanf("%d", &n);
op(0);
for(int i = 1; i <= n; i ++){
int x;
scanf("%d", &x);
pre[i] = pre[i-1] ^ x;
op(pre[i]);
}
printf("%lld", ans);
return 0;
}
再看一道题
P9223 「PEOI Rd1」异或(xor)
题目分析
给定两个正整数
n
,
m
n, m
n,m,求:
∑
i
=
1
n
∑
j
=
1
m
(
i
⊕
j
)
\sum_{i=1}^{n} \sum_{j=1}^{m} (i \oplus j)
i=1∑nj=1∑m(i⊕j)
其中,
⊕
\oplus
⊕ 表示按位异或运算)。答案对 998244353 取模。共T组询问。
1
≤
n
,
m
≤
1
0
16
1 \leq n,m \leq 10^{16}
1≤n,m≤1016,
T
≤
50
T \leq 50
T≤50,
2
54
=
18014398509481984
≈
1.8
×
1
0
16
2^{54} = 18014398509481984 \approx 1.8 \times 10^{16}
254=18014398509481984≈1.8×1016
我们需要计算所有 1 ≤ i ≤ n 1 \leq i \leq n 1≤i≤n 和 1 ≤ j ≤ m 1 \leq j \leq m 1≤j≤m 的 i ⊕ j i \oplus j i⊕j 的总和。直接暴力枚举所有 i i i 和 j j j 时间复杂度为 O ( n m ) O(nm) O(nm),显然不可行。
核心思路:逐位处理,统计所有有贡献的 ( i , j ) (i,j) (i,j) 对数,并乘以该位的权值 2 k 2^k 2k。
- 按位分解:对每一位
k
k
k(从 0 到最大可能的位数 53),统计满足以下条件的
(
i
,
j
)
(i,j)
(i,j) 数量:
- i i i 的第 k k k 位为 0 ( n . n u m 0 ) (n.num0) (n.num0) 且 j j j 的第 k k k 位为 1 ( m . n u m 1 ) (m.num1) (m.num1)。
- i i i 的第 k k k 位为 1 ( n . n u m 1 ) (n.num1) (n.num1) 且 j j j 的第 k k k 位为 0 ( m . n u m 0 ) (m.num0) (m.num0)。
- 根据乘法原理,总数为 n . n u m 0 × m . n u m 1 + n . n u m 1 × m . n u m 0 n.num0 \times m.num1 + n.num1 \times m.num0 n.num0×m.num1+n.num1×m.num0。
- 贡献计算:将上述总数乘以 2 k 2^k 2k,累加到最终答案中。
- 如何高效统计某一位上的 0 和 1 的数量?
对于任意整数 x x x 和位 k k k,我们可以利用二进制周期性快速统计 1~x 中该位为 0 或 1 的数量。观察:0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1
- 对于 0 ~ x - 1 这 x 个数,(从最低位第0位开始记),第 k 位的01周期长度为 2 k + 1 2^{k+1} 2k+1. 例如第 0 位的周期为 2 ,第 1 位的周期为4
- 则记半周期为
t
,完整周期的个数则为num = x / (2 * t)
- 仅考虑完整周期,这 x 个数中,0 和 1 的个数均为
num * t
- 对于剩余的一个不完整周期,需要先得到其长度
res = x % (2 * t)
,将res
与半周期t
进行比较。若剩余的数不到半周期,则全是 0,就将res
加到 0 的个数中,num0 = num * t + res
;若剩余的数超过半周期,则半周期为0,剩下的为1,num0 = num * t + t
,num1 = num * t + res - t
- 注意我们找的范围为0 ~ x - 1,所以在最初要将传入的数
x
自增 1,最后要减去 0 ,也就是使num0
减一,即可转换范围为 1 ~ x.
时间复杂度分析
- 每组数据处理 O ( 54 ) O(54) O(54) 位,每组时间复杂度为 O ( 1 ) O(1) O(1)。
- 总体时间复杂度为 O ( T × 54 ) O(T \times 54) O(T×54),可轻松应对 T = 1 0 5 T = 10^5 T=105 的规模。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T;
const ll MOD=998244353;
struct node{
ll num0, num1;
};
// 把所有的模加和模乘换成这两个函数 本题需要极致的mod 不然过不了
ll add(ll a, ll b){
return ((a % MOD+b % MOD) % MOD + MOD) % MOD;
}
ll multi(ll a, ll b){
return (a % MOD) * (b % MOD) % MOD;
}
// 获取1~x在第k位分别为 0 或 1 的个数
node get_num(ll x, int i){
node e;
x ++; // 0~x-1 -> 1~x
ll t = (1ll << i); // 一个0,1周期的长度的一半 也就是一周期内0或1的个数
ll num = x / (2 * t); // 周期个数
ll res = x % (2 * t); // 剩余的不到一个周期的数的个数
if(res <= t){ // 剩余的数不到半周期 则全是0
e.num0 = num * t + res;
e.num1 = num * t;
}else{ // 剩余超过半周期 则半周期为0 剩下的为1
e.num0 = num * t + t;
e.num1 = num * t + res - t;
}
e.num0 --;
return e;
}
void cal(ll x, ll y){
ll ans = 0;
for(int i = 0; i <= 53; i ++){
node n = get_num(x, i);
node m = get_num(y, i);
ans = add(ans, add(multi(multi((1ll<<i), n.num0), m.num1), multi(multi((1ll<<i), n.num1), m.num0)));
// 原公式为 ans+=(1ll<<i)*(n.num0*m.num1+n.num1*m.num0);
}
printf("%lld\n", ans);
}
int main(){
scanf("%d", &T);
while(T --){
ll n, m;
scanf("%lld%lld", &n, &m);
cal(n, m);
}
return 0;
}
看一道构造最大值的异或题
MAX XOR
构造一个非负整数数列 { a 1 , a 2 , ⋯ , a n } \{a_1, a_2, \cdots, a_n\} {a1,a2,⋯,an} 满足 0 ≤ a i ≤ x i 0 \leq a_i \leq x_i 0≤ai≤xi 使得 S = a 1 ⊕ a 2 ⋯ ⊕ a n S = a_1 \oplus a_2 \cdots \oplus a_n S=a1⊕a2⋯⊕an 最大。共 T 组测试数据,每组数据给出数列 { x n } \{x_n\} {xn},求出每组最大的 S S S并输出。保证所有测试数据的 n n n 的总和不超过 1 0 5 10^5 105。
核心思路:按位+贪心
- 贪心策略:
从高位到低位逐位确定最大异或值,优先保证高位为1(因为高位值更大) - 位处理逻辑:
st
:存储当前仍受限制的数字(上界值),fr
:自由数计数器(这些数字可任意设置各个位的数)- 存在自由数时 (
fr >= 1
):- 直接设置当前位为1(自由数可保证该位为1)
- 跳过当前位后续处理
- 无自由数时 (
fr == 0
):- 将数字分为两类:
c
:当前位可自由选择0/1(上界 ≥ 2 bit 2^{\text{bit}} 2bit)cn
:当前位只能选0(上界 < 2 bit 2^{\text{bit}} 2bit)
- 若有可选项(
!c.empty()
):- 设置当前位为1(由
1^0^0^0^0...=1
得来,所以接下来我们只需要在可选项中找一个数使其该位为1,可选项的剩余数该位为0) - 选择上界最大的数字设置当前位为1(保留最大灵活性),并将其低位放入受限数集合(因为其低位仍受限)
- 其余可选项的当前位设为0,那么它们的低位就不再受限,变为自由数(
fr += c.size() - 1
),这样可以使低位的受限尽可能少,自由数尽可能多(贪心) - 更新状态列表为受限数集合(
st = cn
)
- 设置当前位为1(由
- 将数字分为两类:
时间复杂度
外层循环 31位(bit 30~0),内层循环每组数据 O ( n ) O(n) O(n),总复杂度为 O ( 31 × ∑ n ) O(31 \times \sum n) O(31×∑n),满足约束( ∑ n ≤ 1 0 5 \sum n \leq 10^5 ∑n≤105 → 3.1 × 1 0 6 3.1 \times 10^6 3.1×106 操作)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int t, n, ans, fr; // fr = 自由数计数器
void solve(){
vector<int> st; // 存储当前受限数字的上界
ans = 0; // 最大异或结果
fr = 0; // 自由数计数器清零
// 读取输入数据
for(int i = 1; i <= n; i++){
int x;
scanf("%d", &x);
st.push_back(x);
}
// 从高位到低位逐位处理 (30位到0位)
for(int bit = 30; bit >= 0; bit--){
// 情况1:存在自由数时直接设置当前位为1
if(fr >= 1){
ans |= (1 << bit); // 自由数可以保证当前位为1
continue; // 跳过当前位的后续处理
}
vector<int> c, cn; // c: 可选项, cn: 不可选项
ll val = (1LL << bit); // 当前位对应的值
// 分类处理:根据数字上界与当前位值的关系
for(int i = 0; i < st.size(); i++){
if(st[i] >= val)
c.push_back(st[i]); // 可自由选择0或1
else
cn.push_back(st[i]); // 只能选择0
}
// 情况2:没有自由数但有可选项时
if(c.size() >= 1){
ans |= (1 << bit); // 设置当前位为1
// 选择上界最大的数字设置当前位为1
int tmp = *max_element(c.begin(), c.end());
cn.push_back(tmp - (1 << bit)); // 将该数字的低位放入受限集合
// 其余可选项变为自由数
fr += c.size() - 1;
}
// 更新状态为不可选项(继续处理低位)
st = cn;
}
printf("%d\n", ans);
}
int main(){
scanf("%d", &t); // 测试数据组数
for(int i = 1; i <= t; i++){
scanf("%d", &n); // 每组数据的数字个数
solve();
}
}
目前涉及到的都是一些异或的基础题。后面可能会继续更新。