• SPOJ SUBXOR


    SPOJ SUBXOR

    题意

    给定一个由正整数构成的数组, 求 异或和小于k 的子序列的个数.

    题解

    假设答案区间为 [L, R], XOR[L, R] 等价于 XOR[1, L - 1] ^ XOR[1, R], 可以使用 01Trie 保存目前已有的 前缀异或和, 对于每一个新的前缀插入之前, 在 01Trie 中查询 与 新的前缀 异或值 小于 K 的 已有前缀和的个数.

    对于每个TrieNode 的定义为

    struct TrieNode {
        TrieNode* next[2];
        int cnt;
        TrieNode() {
            next[0] = next[1] = NULL;
          	// 保存当前前缀的个数
            cnt = 0;
        }
    };
    

    在进行查询时, 比较 新的前缀和 and k 的每一位

    已有前缀和的第 i 位 indexPre( 新的前缀和的第 i 位) indexK( K 的第 i 位) 相应操作
    0 0 0 递归求解左子树
    1 0 1 统计左子树叶子节点个数, 递归求解右子树
    1 1 0 递归求解右子树
    0 1 1 统计右子树叶子节点个数, 递归求解左子树

    对于 indexPre == 0, indexK == 0 的情况来说, 已有前缀和为 0 时满足条件, 因此需要递归求解左子树. 当已有前缀和为 1 时, indexK == 1, 大于要求的值, 所以不继续递归.

    对于 indexPre == 0, indexK == 1 的情况来说, 已有前缀和为 1 时满足条件, 但 右子树 中可能有 值大于等于 K 的叶子节点, 因此需要递归求解右子树. 当已有前缀和为 0 时, indexK == 0, 所有左子树的叶子节点的值均小于 K, 因此统计左子树叶子节点的个数

    AC代码

    #include <cstdio>
    #include <iostream>
    using namespace std;
    struct TrieNode {
        TrieNode* next[2];
        int cnt;
        TrieNode() {
            next[0] = next[1] = NULL;
            cnt = 0;
        }
    };
    void insertNum(TrieNode* root, unsigned num) {
        TrieNode* p = root;
        for(int i = 31; i >= 0; i--) {
            int index = (num >> i) & 1;
            if(!p->next[index])
                p->next[index] = new TrieNode();
            p = p->next[index];
            p->cnt++;
        }
    }
    int getCnt(TrieNode* root) {
        return root ? root->cnt : 0;
    }
    int queryLessThanK(TrieNode* root, int pre, int k) {
        TrieNode* p = root;
        int ret = 0;
        for(int i = 31; i >= 0; i--) {
            if(p == NULL)
                break;
            int indexPre = (pre >> i) & 1; // prefiexbit
            int indexK = (k >> i) & 1; // bit
            if(indexPre == indexK) {
                if(indexK)
                    ret += getCnt(p->next[1]);
                p = p->next[0];
            }
            else if(indexPre != indexK) {
                if(indexK)
                    ret += getCnt(p->next[0]);
                p = p->next[1];
            }
        }
        return ret;
    }
    int main() {
        int nTest; scanf("%d", &nTest);
        while(nTest--) {
            int nNum, k;
            scanf("%d %u", &nNum, &k);
            TrieNode* root = new TrieNode();
            // insertNum(root, 0) 保证了前缀异或和 pre 自身 可以小于 k
            insertNum(root, 0);
            unsigned pre = 0;
            long long ans = 0;
            while(nNum--) {
                unsigned num; scanf("%u", &num);
                pre = pre ^ num;
                ans += queryLessThanK(root, pre, k);
                insertNum(root, pre);
            }
            cout << ans << endl;
        }
        return 0;
    }
    
    
  • 相关阅读:
    《iBoard 是什么》之简介
    [iBoard 电子学堂][第八卷 设计任意波发生器]第一篇 iBoard 任意波发生器简介
    [iBoard 电子学堂][第〇卷 电子基础]第二篇 电路图与印刷电路板
    职业规划提示
    督查督办工作基本程序
    今天收拾了个电脑抽屉,发现原来我是个有钱人
    VB之SendKeys键盘模拟
    天骄辅助外挂制作,寻求合作
    用CE找武林外传一级基址的方法
    asp实现数据记录的备份及恢复抛砖引玉
  • 原文地址:https://www.cnblogs.com/1pha/p/8726928.html
Copyright © 2020-2023  润新知