zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    制作一个html中闪动的title 来提示消息
    Unicode与 utf8的互相转换
    程序员找女友的类
    使用php将数组转为XML
    自己动手画一个HTML5的按钮
    浏览器推通知给桌面
    如何使用定时任务
    封装之property,多态,鸭子类型,classmethod与staticmethod
    面向对象
    继承与派生
  • 原文地址:https://www.cnblogs.com/1pha/p/8726928.html
Copyright © 2011-2022 走看看