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;
}

posted @ 2018-04-06 09:55  1pha  阅读(224)  评论(0编辑  收藏  举报