注意到题目中的一句话:
让其中一段连续序列数字反转——0 变 1,1 变 0(操作 1)
数字反转,想到了什么?异或!
区间修改,单点查询,想到了什么?树状数组!
树状数组可以非常优秀的维护此类操作(具有区间可加可减性)!
以下是详细的分析,所以上面那段话不是很明白也木有什么大问题qwq。
操作1
我们可以用 xor 完成这个操作。如果不明白二进制下是怎样操作的也木有关系,您只需要知道:二进制下,按位进行 xor 运算的值为 1 当且仅当进行 xor 运算的两个位上的数字一个为 1 ,一个为 0 (或理解为:二进制下 xor 运算中,两位相同则为 0 ,不同则为 1 。例如:((101)_2 ext{xor} (110)_2 = (011)_2))。
类比上面 xor 运算的定义,对于把一段 01 串翻转,您有什么猜想?
我们可以通过把一段 01 串对 1 进行 xor 得到结果。因为 xor 运算一个很好的性质:对于这个 01 串,我们希望使 0 变成 1 ,那么 0 对 1 xor 得到 1 ;同时希望使 1 变成 0 ,那么 1 对 1 xor 得到 0!
所以我们需要维护一个异或差分树状数组 (c),当对 ([l,r]) 这个 01 串反转时,先把异或差分树状数组中 (c_{l..n} ext{xor} 1) ,那么这个时候也就相当于翻转了 01 串 ([l,n]) 。但是我们只需要将 ([l,r]) 翻转,所以我们需要一种操作来抵消刚刚的操作中 ([r+1,n]) 。那么如何抵消 ([r+1,n]) 的翻转呢?很简单,只要将 (c_{(r+1)..n} ext{xor} 1) 即可。对于 ([r+1,n]) 来说做了两次异或,也就相当于做了两次翻转,那么就相当于木有翻转,也就抵消了!
操作2
如果您可以理解操作1的原理,那么操作2也就很简单了。
注意到差分与前缀和互为逆运算,所以在单点求值时做一遍异或前缀和即可。如果不熟悉差分与前缀和的转换可以看这里:https://www.cnblogs.com/BlueInRed/p/12501868.html
于是我们有完整AC代码:
#include <cstdio>
#define lowbit(x) x & -x
int n, m, a[5 * 100010], c[5 * 100010];
void add(int x)
{
for(int i=x; i<=n; i += lowbit(i))
c[i] ^= 1;
}
int ask(int x)
{
int ans = 0;
for(int i=x; i; i -= lowbit(i))
ans ^= c[i];
return ans;
}
int main(void)
{
scanf("%d%d", &n, &m);
while(m--)
{
int opt, l, r;
scanf("%d", &opt);
if(opt == 1)
{
scanf("%d%d", &l, &r);
add(l);
add(r+1);
}
else
{
scanf("%d", &l);
printf("%d
", ask(l));
}
}
return 0;
}