bzoj 4888 [Tjoi2017]异或和 树状数组

题面

题目传送门

解法

枚举最后的二进制位,假设是第\(k\)

问题就转化成有多少段和二进制意义下第\(k\)位是1

不妨转化成前缀和

变成有多少对\(i,j(i>j)\),使得\(s_i-s_j\)的第\(k\)位是1

我们可以枚举\(k\),假设\(s_i\)的第\(k\)位是1

如果\(s_i-s_j\)的第\(k\)位是1,那么存在两种情况:

1.\(s_j\)的第\(k\)位是0,且不存在减法退位这种情况

2.\(s_j\)的第\(k\)位是1,出现了减法退位

那么我们只要比较前\(k-1\)位的情况即可

用权值树状数组统计一下即可

扫描二维码关注公众号,回复: 2766360 查看本文章

\(s_i\)的第\(k\)位是0同理

那么只要开两个权值树状数组就可以解决问题

时间复杂度:\(O(n\ log^2\sum a_i)\)

代码

#include <bits/stdc++.h>
#define M 1000010
#define N 100010
using namespace std;
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int n, a[N], s[N], f[2][M];
int lowbit(int x) {return x & -x;}
void modify(int k, int x) {
    for (int i = x + 1; i <= s[n] + 1; i += lowbit(i))
        f[k][i]++;
}
int query(int k, int x) {
    int ret = 0;
    for (int i = x + 1; i; i -= lowbit(i))
        ret += f[k][i];
    return ret;
}
int calc(int l, int r, int k) {
    return query(k, r) - query(k, l);
}
int main() {
    int ans = 0; read(n);
    for (int i = 1; i <= n; i++)
        read(a[i]), s[i] = s[i - 1] + a[i];
    for (int k = 0; (1 << k) <= s[n]; k++) {
        int sum = 0, lim = (1 << k) - 1;
        for (int i = 0; i <= n; i++) {
            int t = (s[i] >> k) & 1, las = s[i] & lim;
            if (t) sum = (sum + calc(-1, las, 0) + calc(las, lim, 1)) % 2, modify(1, las);
                else sum = (sum + calc(-1, las, 1) + calc(las, lim, 0)) % 2, modify(0, las);
        } 
        if (sum) ans |= 1 << k;
        memset(f, 0, sizeof(f));
    }
    cout << ans << "\n";
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/copperoxide/p/9478376.html