• 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
  • 相关阅读:
    JS内容左右滑动
    JS返回上一页
    两栏 三栏的css
    舅舅去世
    .net学习开始
    以论坛管理的方式来处理公司资讯
    《尽管去做》摘
    网页视频播放器代码集
    火影忍者和海贼王
    古代风水文献
  • 原文地址:https://www.cnblogs.com/liuweimingcprogram/p/5884755.html
Copyright © 2020-2023  润新知