E. Beautiful Subarrays 字典树

http://codeforces.com/contest/665/problem/E

给定一个序列,问其中有多少个区间,所有数字异或起来 >= k

看到异或,就应该想到异或的性质,A^B^B = A,所以,用sum[i]表示1--i的异或值,那么i...j的异或值就是sum[j] ^ sum[i - 1]

所以原问题转化为在一个数组中,任选两个数,异或值 >= k

首先所有数字 <= 1e9

那么前缀异或值 <= 2^31 - 1

那么用int就够了,从30位开始插入字典树,(1 << 31)是溢出的

查找的时候,分两类

1、当前的位置中k的二进制位是0,就是第15位,如果k的二进制位是0,那么往反方向走字典树的子树总是可以得,这个时候只需要加上去子树大小即可。

2、如果那一位k是1,我们没法马上判断贡献,那么只能往反方向贪心走字典树。

注意的就是叶子节点有可能没走到,需要最后加上来。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;

#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 1e6 + 20;
const int N = 2;
int a[maxn];
int sum[maxn];
int n, k;
struct node {
    struct node *pNext[N];
    int cnt;
}tree[maxn * 40];
int t;
struct node * create() {
    struct node *p = &tree[t++];
    for (int i = 0; i < N; ++i) {
        p->pNext[i] = 0;
    }
    p->cnt = 0;
    return p;
}
void insert(struct node **T, int val) {
    struct node *p = *T;
    if (p == NULL) {
        p = *T = create();
    }
    for (int i = 30; i >= 0; --i) {
        int id = (val & (1 << i)) > 0;
        if (p->pNext[id] == NULL) {
            p->pNext[id] = create();
        }
        p = p->pNext[id];
        p->cnt++;
    }
    return ;
}
LL find(struct node *T, int val) {
    struct node *p = T;
    LL ans = 0;
    if (p == NULL) return ans;
    for (int i = 30; i >= 0; --i) {
        int id = (val & (1 << i)) > 0;
        int id_k = (k & (1 << i)) > 0;
        if (id_k == 0) {
            if (p->pNext[!id] != NULL)
                ans += p->pNext[!id]->cnt; //这边的树全部都可以
            if (p->pNext[id] == NULL) return ans;
            p = p->pNext[id]; //如果走到了叶子节点,我这里return了,不会重复增加
        } else { //id_k = 1
            if (p->pNext[!id] == NULL) return ans;
            p = p->pNext[!id];
        }
    }
    return ans + p->cnt; //叶子节点有可能没算到,参考样例1
}
void work() {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        sum[i] = sum[i - 1] ^ a[i];
//        cout << sum[i] << " ";
    }
//    printf("%d
", 1 << 30);
    struct node *T = NULL;

    insert(&T, sum[1]);
    LL ans = 0;
    for (int i = 2; i <= n; ++i) {
        ans += find(T, sum[i]);
        insert(&T, sum[i]);
//        cout << find(T, sum[i]) << endl;
    }
    for (int i = 1; i <= n; ++i) ans += sum[i] >= k;
    cout << ans << endl;
    return;
}
int main() {
#ifdef local
    freopen("data.txt","r",stdin);
#endif
    work();
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/liuweimingcprogram/p/5884755.html