1803. 统计异或值在范围内的数对有多少

本文介绍了一道LeetCode题目,要求统计数组中异或值在特定范围内的数对。通过分析数据范围,发现不能直接暴力枚举,而是利用Trie树进行优化。文章详细解释了如何使用Trie树进行边插入边查询,并通过前缀和思想解决查询难点,实现空间复杂度为O(NlogS)的解决方案。文章还提供了AC代码,便于读者理解和复习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

统计异或值在范围内的数对有多少
给你一个整数数组 nums (下标 从 0 开始 计数)以及两个整数:low 和 high ,请返回 漂亮数对 的数目。

漂亮数对 是一个形如 (i, j) 的数对,其中 0 <= i < j < nums.length 且 low <= (nums[i] XOR nums[j]) <= high 。

  • 输入输出

输入:nums = [1,4,2,7], low = 2, high = 6
输出:6
解释:所有漂亮数对 (i, j) 列出如下:
- (0, 1): nums[0] XOR nums[1] = 5
- (0, 2): nums[0] XOR nums[2] = 3
- (0, 3): nums[0] XOR nums[3] = 6
- (1, 2): nums[1] XOR nums[2] = 6
- (1, 3): nums[1] XOR nums[3] = 3
- (2, 3): nums[2] XOR nums[3] = 5

  • 数据范围

1 <= nums.length <= 2 * 104
1 <= nums[i] <= 2 * 104
1 <= low <= high <= 2 * 104

思路分析

闲扯

这道题给人的第一感觉就是直接暴力枚举,一看数据范围就知道还是刷题不够,这个2e4范围明显不能暴力枚举,赛后听群里大佬说用Trie树,虽然知道Trie树的基本用法,但还是不会bushi,看了题解才发现只会用Trie树求最大异或对是远远不够的

思路

  • 我们使用Trie树来存储 (1-i-1) 的所有数,第i个数为x,这里采用边插入边查询,即每次插入x之前查询 (1-i-1) 中满足与x异或在范围内的,所以需要开辟的空间为:O(NlogS)
  • 查询操作的时候如何查找初符合x^u在范围内的数量是这道题的难点,可以采用前缀和的思想:找出 x ^ u<=high的u的数量r,满足 x ^u <=low-1 的数量l,则对于插入的第i个数,1-i-1 中满足的数为: r-l
  • 问题转化为求x ^u<=limit 的u的个数,这种写法我一直wa掉,最后借鉴了大佬的<limit 的写法,这样写法很简明,以后建议使用
  • 具体的Trie树操作可以去了解一下,这个数据结构处理异或的一些操作很有效

AC代码(带注释,方便复习)

class Solution {
public:
    static const int N=20010*20;
    int tr[N][2],cnt[N],idx=0;
    void insert(int x)
    {
        int p=0;
        for(int i=16;i>=0;i--){
            int u=x>>i&1;
            if(!tr[p][u]) tr[p][u]=++idx;
            p=tr[p][u];
            cnt[p]++;
        }
    }
    int query(int x,int limit)
    {
        int p=0;
        int res=0;
        for(int i=16;i>=0;i--){
            int u=x>>i&1,v=limit>>i&1;
            //这种写法可以发现res累加的都是异或后小于limit的,等于的都没有选择,所以limit的范围要大一
            //因为每次有两种选择时,我都是当前的cur与limit相等,
            //所以每次累加的都是异或后<limit的,最后统计的总和也是异或后小于limit的,
            if(u == 0 && v == 0) p = tr[p][0];
            else if(u == 0 && v == 1) res += cnt[tr[p][0]], p = tr[p][1];
            else if(u == 1 && v == 0) p = tr[p][1];
            else if(u == 1 && v == 1) res += cnt[tr[p][1]], p = tr[p][0];
            if(!p) break;
        }
        return res;
    }
    int countPairs(vector<int>& nums, int low, int high) {
        int res = 0;
        for(auto &x : nums) {
        //这里要先查询在插入,因为x^x =0也是满足的
            res += query(x, high+1) - query(x, low);
            insert(x);
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值