CodeForces - 665E Beautiful Subarrays

这道题其实可以用前缀和。异或运算有\(pre[r]\) \(xor\) \(pre[l-1]==a[l]\) \(xor\) \(a[l+1]\) \(xor...xor\) \(a[r]\)

我们把前缀和二进制分解插入字典树上,模拟异或运算。将新的前缀和与之前的前缀和匹配。

解释一下代码吧。

设当前询问的前缀和为\(x\)。首先我们必须保证\(i-1\)位异或后大于等于\(k\)\(i-1\)位。就先这样假设。

  • \(k\)\(i\)位是\(0\):我们知道如果这一位我们选择\(x[i]\) \(xor\) \(1\)的方向,这一位异或的答案会是\(1\),那么在这之后的所有情况一定都是合法的,我们直接累加,跳过即可。显然我们选择\(x[i]\)这个方向。
  • \(k\)\(i\)位是\(1\):我们只能使异或答案为\(1\)。选择\(x[i]\) \(xor\) \(1\)的方向。

这样下去我们最后走出来的东西一定与\(k\)相等。(当然可能这样的串不存在)加上\(siz\)即可。

还有一点,因为是前缀和,我们需要提前插入\(0\)

#include<cstdio>

const int N = (1 << 24) + 2;

int n, k, sum, ch[N][2], siz[N], cnt = 1; 
long long ans;

int read() {
    int x = 0, f = 1; char s;
    while((s = getchar()) > '9' || s < '0') {if(s == '-') f = -1;}
    while(s <= '9' && s >= '0') {
        x = (x << 1) + (x << 3) + (s ^ 48);
        s = getchar();
    }
    return x * f;
}

void insert(const int x) {
    int p = 1;
    for(int i = 30; i >= 0; -- i)  {
        int op = (x >> i) & 1;
        if(! ch[p][op]) ch[p][op] = ++ cnt;
        ++ siz[p]; p = ch[p][op];                 
    }
    ++ siz[p];
}

void ask(const int x) {
    int p = 1;
    for(int i = 30; i >= 0; -- i) {
        int op = (x >> i) & 1;
        if(! ((k >> i) & 1)) ans += siz[ch[p][op ^ 1]], p = ch[p][op];
        else p = ch[p][op ^ 1];
    }
    ans += siz[p];
}

int main() {
    n = read(), k = read();
    insert(0);
    for(int i = 1; i <= n; ++ i) {
        sum ^= read();
        ask(sum); insert(sum);
    }
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/AWhiteWall/p/12348795.html