P6225

P6225异或橙子

题意

一个长度为 \(n\) 的序列,每个数有一个初始值 \(a_i\),维护以下 \(q\) 个操作:

  • 单点修改
  • 给定区间 \([l, r]\) 求其所有子区间的异或和

对于所有数据,\(0\le a_i\le 10^9,1\le n,q\le 2\times 10^5\)

分析

01-枚举

对于每一次询问,暴力枚举其所有子区间,时间复杂度 \(O(qn^2)\) ,无法接受,故尝试分析特殊性质。

02-特殊性质

考虑到运算符为异或,而异或运算有以下性质:

  • x ^ x = 0
  • x ^ 0 = x

得到启发:

如果所有子区间中一个数出现了偶数次,那么这个数不影响答案。
反之,若出现奇数次,则计算答案。

03-正解

考虑一个长度为偶数区间 \([a_1,\ldots ,a_4]\) 的所有子区间,统计每一个数的操作次数:

区间长度 \(a_1\) \(a_4\) \(a_3\) \(a_4\)
1 1 1 1 1
2 1 2 2 1
3 1 2 2 1
4 1 1 1 1
总计 4 6 6 4
  • 发现每一个数都出现了偶数次,答案为 \(0\)
  • 显而易见对于所有偶数长度的区间都成立。

考虑一个长度为奇数区间 \([a_1,\ldots ,a_5]\) 的所有子区间,统计每一个数的操作次数:

区间长度 \(a_1\) \(a_2\) \(a_3\) \(a_4\) \(a_5\)
1 1 1 1 1 1
2 1 2 2 2 1
3 1 2 3 2 1
4 1 2 2 2 1
5 1 1 1 1 1
总计 5 8 9 8 5
& \(1\) 1 0 1 0 1
  • 发现对于一个长度为奇数的区间,只需要考虑所有奇数项的异或和即可。
  • 显而易见,对于所有长度为奇数的区间都成立。

综上,只需要一个维护区间异或和的数据结构就可以解决本题。

04-实现

考虑用两个树桩数组解决问题:

  • 第一个:维护前缀异或和(即区间异或和)
  • 第二个:维护奇数项前缀异或和

当然,还有另一种方式;

  • 第一个:维护偶数项前缀异或和
  • 第二个:维护奇数项前缀异或和

它们的原理相同,难度相似,我选用第一种(只写了这个)。

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 5;

int n, q;
int a[N];

struct bitree { //写的真好
    int c[N], res;
    inline int lb(int x) {
        return x & (-x);
    }
    inline void add(int x, int v) {
        for (; x <= n; x += lb(x))
            c[x] ^= v;
    }
    inline int find(int x) {
        for (res = 0; x; x -= lb(x))
            res ^= c[x];

        return res;
    }
    inline int find(int x, int y) {
        return find(y) ^ find(x - 1);
    }
} tr, tr1;

signed main() {

    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> n >> q;

    for (int i = 1; i <= n; i++) {
        cin >> a[i], tr.add(i, a[i]);

        if (i & 1) tr1.add(i, a[i]);
    }

    for (int op, x, y; q--; ) {
        cin >> op >> x >> y;

        if (op == 1) {
            tr.add(x, a[x] ^ y);
						// a[x] : 消除原来的影响
						// y : 更改后的值
            if (x & 1) tr1.add(x, a[x] ^ y);

            a[x] = y; 
            //记录更改后的值

        } else {
            if ((y - x + 1) & 1) {
                int res = tr1.find(x, y);

                if (!(x & 1)) res ^= tr.find(x, y);
									//   1 1 1 1 1
									// ^ 1 0 1 0 1
									//-------------
									//   0 1 0 1 0
									
                cout << res << endl;

            } else cout << 0 << endl;
        }
    }


    return 0;
}

QAQ

Written with StackEdit中文版.

posted @ 2024-03-29 13:26  afhuds  阅读(3)  评论(0编辑  收藏  举报