1803. 统计异或值在范围内的数对有多少 - 力扣(Leetcode)
题意:
思路:
- 很经典的方法,01字典树,同时在树上多维护一个数据,有多少个节点经过当前节点,记为cnt
- 我们可以写出一个query(x,tar)函数,x时num数组中的数,答案就是query(x,high)- query(x,low-1)
- 构造一棵空树,然后在每个节点插入,按照 x 向下查找位置时,记录经过的点在之前有多少个节点经过(因为题目有一个i < j 的条件),然后讨论从高位往低位
- tar 当前第 i 位二进制为 1 时,说明异或对结果的第 i 位可以为 0 或 1。往异或结果为 0 的子节点走,之后所有的必然都比 tar 小,可以直接加上这个子节点的cnt;往异或结果为 1 的子节点走,之后需再判断
- tar 当前第 i 位二进制为 0 时,说明异或对结果的第 i 位只能为 0。只能往对应的子节点走
- 在上面的走法,如果无法走,就停止,返回已经算出来的数量
- 像这样计算一个,插入一个就能得到答案了
const int N = 20005; int trie[N * 15][2], cnt[N * 15], idx; class Solution { public: int countPairs(vector<int>& nums, int low, int high) { return get(nums, high) - get(nums, low - 1); } int get(vector<int>& nums, int high) { idx = 0; memset(trie, 0, sizeof(trie)); memset(cnt, 0, sizeof(cnt)); int ans = 0; for (int i = 0; i < nums.size(); i++) { ans += query(nums[i], high); add(nums[i]); } return ans; } void add(int x) { int p = 0; for (int i = 14; i >= 0; i--) { int u = (x >> i) & 1; if (trie[p][u] == 0) trie[p][u] = ++idx; p = trie[p][u]; //移动到下一个结点 cnt[p]++; // 个数增加,cnt[x]代表x结点出现的次数 } } int query(int x, int high) { int sum = 0, p = 0; for (int i = 14; i >= 0; i--) { int u = (x >> i) & 1; if (((high >> i) & 1) == 1) { //high当前i位为1, 那么x与以前数当前i位的异或可以位1或者0 sum += cnt[trie[p][u]];//加上与x异或后当前i位为0的数量 if (trie[p][u ^ 1] == 0) return sum; //没有结点可以继续走下去,直接返回 p = trie[p][u ^ 1]; //继续往异或的结点走下去 } else { //high当前i位为0, x与以前数异或的第i为必须为0 if (trie[p][u] == 0) return sum; //没有结点走下去 p = trie[p][u]; //寻找与x的第i位相同的进制,异或结果为0 } } sum += cnt[p]; //加上走到最后的结点数 return sum; } };
标签:cnt,01,nums,int,high,trie,异或,字典 From: https://www.cnblogs.com/cfddfc/p/17032530.html