题目地址:
https://www.acwing.com/problem/content/1270/
有一个
n
n
n个元素的数组,每个元素初始均为
0
0
0。有
m
m
m条指令,要么让其中一段连续序列数字反转——
0
0
0变
1
1
1,
1
1
1变
0
0
0(操作
1
1
1),要么询问某个元素的值(操作
2
2
2)。例如当
n
=
20
n=20
n=20时,
10
10
10条指令如下:
输入格式:
第一行包含两个整数
n
,
m
n,m
n,m,表示数组的长度和指令的条数;
以下
m
m
m行,每行的第一个数
t
t
t表示操作的种类:
若
t
=
1
t=1
t=1,则接下来有两个数
L
,
R
L,R
L,R,表示区间
[
L
,
R
]
[L,R]
[L,R]的每个数均反转;
若
t
=
2
t=2
t=2,则接下来只有一个数
i
i
i,表示询问的下标(数组下标从
1
1
1开始)。
输出格式:
每个操作
2
2
2输出一行(非
0
0
0即
1
1
1),表示每次操作
2
2
2的回答。
数据范围:
1
≤
n
≤
1
0
5
1≤n≤10^5
1≤n≤105
1
≤
m
≤
5
×
1
0
5
1≤m≤5×10^5
1≤m≤5×105
保证
L
≤
R
L≤R
L≤R
由于翻转等价于不进位加法,从而等价于异或,所以我们考虑一个差分数组,并用树状数组维护之。这个差分数组的差分取的是异或(即 A A A的差分数组 c [ i ] = A [ i ] ∧ A [ i − 1 ] c[i]=A[i]\land A[i-1] c[i]=A[i]∧A[i−1])。那么翻转 A [ l : r ] A[l:r] A[l:r]等价于 c [ l ] = c [ l ] ∧ 1 , c [ r + 1 ] = c [ r + 1 ] ∧ 1 c[l]=c[l]\land 1, c[r+1]=c[r+1]\land 1 c[l]=c[l]∧1,c[r+1]=c[r+1]∧1,询问等价于求前缀异或和。代码如下:
#include <iostream>
#define lowbit(x) (x & -x)
using namespace std;
const int N = 1e5 + 10;
int n, m;
int tr[N];
void add(int k, int x) {
for (; k <= n; k += lowbit(k)) tr[k] ^= x;
}
int sum(int k) {
int res = 0;
for (; k; k -= lowbit(k)) res ^= tr[k];
return res;
}
int main() {
scanf("%d%d", &n, &m);
while (m--) {
int t, l, r, k;
scanf("%d", &t);
if (t == 1) {
scanf("%d%d", &l, &r);
add(l, 1);
add(r + 1, 1);
} else {
scanf("%d", &k);
printf("%d\n", sum(k));
}
}
}
每次操作时间复杂度 O ( log n ) O(\log n) O(logn),空间 O ( n ) O(n) O(n)。