【做题笔记】P5057[CQOI2006]简单题

注意到题目中的一句话:

让其中一段连续序列数字反转——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;
}
原文地址:https://www.cnblogs.com/BlueInRed/p/12644177.html