当前位置: 首页 > news >正文

算法模板2:位运算+离散化+区间合并

文章目录

      • 1.6 位运算
      • **位运算的常见应用**
      • 1.7 离散化
        • **经典离散化题目例子**
          • **1. 区间合并和覆盖长度问题**
          • **2. 区间查询与修改**
          • **3. 动态求第 K 小值**
          • **4. 区间最大重叠次数**
          • **5. 动态逆序对计数**
          • **6. 二维区间问题**
          • **7. 模拟车流/时间段事件**
          • **8. 区间众数统计**
        • **具体例题**
      • 1.8 区间合并

1.6 位运算

位运算是直接在二进制位上进行操作的运算。位运算通常用于优化算法,尤其是当涉及到数字处理、快速计算和空间优化

  1. 按位与 & (AND)
  • 操作规则:当两个二进制数的对应位都为1时,结果才为1,否则为0。

    1010
    & 1101
    ------
    1000
    
    • 检查某一位是否为1: 例如,n & (1 << k) 判断第k位是否为1。如果为1,结果不为0;否则为0。
    • 清除最低位的1n & (n - 1) 会将n的最低位1变成0。
  1. 按位或 | (OR)
  • 操作规则:当两个二进制数的对应位中至少有一个为1时,结果为1。

    1010
    | 1101
    ------
    1111
    
    • 设置某一位为1n | (1 << k) 将第k位置为1。
  1. 按位异或 ^ (XOR)
  • 操作规则:当两个二进制数的对应位相异时,结果为1,否则为0。

    1010
    ^ 1101
    ------
    0111
    
    • 翻转某一位n ^ (1 << k) 将第k位的值翻转(1变成0,0变成1)。
    • 检查两个数是否相等a ^ b == 0 表示a与b相等。
  1. 按位非 ~ (NOT)
  • 操作规则:对每一位取反,1变成0,0变成1。

    ~ 1010
    ------
    0101
    
  1. 左移 << (Left Shift)
  • 操作规则:将二进制数的所有位向左移动指定的位数,左移时低位补0,高位丢弃。

    1010 << 1 = 10100
    
    • 乘以2n << 1 相当于将n乘以2。
    • 高速计算乘法:左移可以用来快速计算n的2的幂次方倍数。
  1. 右移 >> (Right Shift)
  • 操作规则:将二进制数的所有位向右移动指定的位数,右移时高位补符号位(有符号数时补1,没符号数时补0)。

    1010 >> 1 = 0101
    
    • 除以2n >> 1 相当于将n除以2。
    • 高速计算除法:右移可以用来快速计算n的2的幂次方除法。
  1. 获取某一位的值
n >> k & 1
  • 说明n >> k 将n右移k位,然后与1做按位与运算,这样可以获取n的第k位数字(0或1)。
  1. 获取n的最后一位1
lowbit(n) = n & -n
  • 说明n & -n 用来得到n的最后一位1。例如,n = 12 (1100) 时,lowbit(n) 的结果为 4 (0100)
    • -n 是n的二进制补码表示,n & -n 会保留n的最后一位1,并将其他所有位清零。

位运算的常见应用

  1. 判断奇偶性
    • 通过 n & 1 判断n是否为奇数(如果结果是1则n为奇数)。
  2. 快速计算2的幂
    • 使用 1 << k 来计算 2^k
  3. 检查二进制表示中是否只有一个1
    • n & (n - 1) == 0 可以检查n是否为2的幂(如果n只包含一个1)。
  4. 统计二进制中1的个数
    • __builtin_popcount(n)while (n) {n &= (n - 1);},每次去掉最右边的1。
  5. 交换两个数
    • a ^= b; b ^= a; a ^= b; 可以用异或交换两个数。
  6. 清除低位的1
    • n & (n - 1) 会把n的最低位的1清除。
  7. 模拟集合
    • 通过位运算可以模拟集合的操作,如 setunionintersection 等。例如,n 可以表示一个集合,其中第k位表示是否包含元素k。

1.7 离散化

核心作用:将数值范围较大的数据映射到一个较小的连续整数范围,便于在数组、前缀和、差分、树状数组、线段树等数据结构中操作。

主要解决值域大、数据稀疏的问题,结合其他算法实现高效求解,如扫描线、差分数组、树状数组和线段树等。

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{int l = 0, r = alls.size() - 1;while (l < r){int mid = l + r >> 1;if (alls[mid] >= x) r = mid;else l = mid + 1;}return r + 1; // 映射到1, 2, ...n
}
经典离散化题目例子
1. 区间合并和覆盖长度问题

题目:给定 n 个区间 [li,ri][l_i, r_i],求所有区间的覆盖总长度(去重后的长度)。

  • 分析:原区间可能范围很大(如 [1,109][1, 10^9]),无法直接操作。通过离散化,将所有区间端点压缩到较小范围。

  • 思路

    1. 离散化所有区间的端点。
    2. 用差分数组或扫描线记录区间变化,统计覆盖长度。
2. 区间查询与修改

题目:有 n 个点,每次给区间 [l,r][l, r] 增加一个值 cc,并查询某个点的最终值。

  • 分析:区间范围过大(如 1∼1091 \sim 10^9),通过离散化压缩到有限范围。

  • 思路

    1. 离散化所有可能的坐标。
    2. 使用差分数组、树状数组或线段树进行区间操作。
3. 动态求第 K 小值

题目:有 n 个操作,每次插入一个值或查询当前数据的第 k 小值。

  • 分析:可能有很大的值范围(如 [1,109][1, 10^9]),通过离散化解决大范围权值问题。

  • 思路

    1. 离散化所有插入的值。
    2. 用树状数组或线段树统计权值频次。
4. 区间最大重叠次数

题目:给定 n 个区间,求区间重叠最多的时间点及重叠次数。

  • 分析:时间点范围可能很大,需离散化区间端点。

  • 思路

    1. 离散化所有区间端点。
    2. 使用扫描线或差分数组统计每个离散点的重叠次数。
5. 动态逆序对计数

题目:给定一个初始数组,每次插入一个值,实时输出数组的逆序对个数。

  • 分析:插入值可能范围过大(如 [1,109][1, 10^9]),需离散化值域。

  • 思路

    1. 离散化所有可能的值。
    2. 用树状数组或线段树动态维护逆序对。
6. 二维区间问题

题目:给定 n 个矩形,求哪些矩形有重叠区域或总覆盖面积。

  • 分析:矩形坐标范围可能很大,需对 x 和 y 坐标分别离散化。

  • 思路

    1. 对 x 和 y 轴分别离散化,压缩到二维数组。
    2. 使用二维差分或扫描线处理。
7. 模拟车流/时间段事件

题目:给定 n 个车辆的进入和离开时间,求某时刻停车场中车辆的数量。

  • 分析:时间范围可能很大(如 [1,109][1, 10^9]),需离散化时间点。

  • 思路

    1. 离散化所有进入和离开的时间点。
    2. 使用差分数组统计每个时间点的车辆变化。
8. 区间众数统计

题目:给定一个数组,支持动态查询任意区间内的众数。

  • 分析:值域较大(如 [1,109][1, 10^9]),需离散化元素值。

  • 思路

    1. 离散化数组中的值。
    2. 用莫队算法或树状数组分块统计。
具体例题
  1. 区间统计问题:Leetcode 56 合并区间
  2. 动态第 k 小问题:Leetcode 315 计算右侧小于当前元素的个数
  3. 区间重叠次数:AcWing 1240 最大区间重叠
  4. 动态逆序对计数:Leetcode 493 翻转对
  5. 二维差分矩阵:AcWing 1226 最大矩形面积

1.8 区间合并

using PII = pair<int, int>;// 合并区间的函数
void merge(vector<PII> &segs) {vector<PII> res;// 1. 按起点排序,如果起点相同,按终点排序sort(segs.begin(), segs.end());// 2. 初始化st和ed为不可能出现的值int st = -2e9, ed = -2e9;// 3. 遍历所有区间for (auto seg : segs) {if (ed < seg.first) { // 当前区间与前一个区间不重叠if (st != -2e9) res.push_back({st, ed}); // 把上一个区间加入结果st = seg.first;  // 更新sted = seg.second; // 更新ed} else { // 当前区间与前一个区间重叠ed = max(ed, seg.second); // 合并区间,更新ed}}// 4. 最后一个区间加入结果if (st != -2e9) res.push_back({st, ed});egs = res; // 更新输入数组
}
http://www.lryc.cn/news/490869.html

相关文章:

  • 钉钉授权登录
  • 【视频】二维码识别:libzbar-dev、zbar-tools(zbarimg )
  • C语言中的结构体,指针,联合体的使用
  • 基于卡尔曼滤波器的 PID 控制
  • CVE-2022-26201
  • 海信Java后端开发面试题及参考答案
  • 传智杯 3-初赛:终端
  • 大数据新视界 -- Hive 数据分区:精细化管理的艺术与实践(上)(7/ 30)
  • 【中间件】Redis
  • RTSP播放器EasyPlayer.js播放器分辨率高的视频在设置container的宽高较小时,会出现锯齿状的画面效果
  • Java爬虫:获取商品详情的实践之旅
  • 行业分析---2024年小鹏汽车AI Day及三季度财报
  • 写时复制,读时加载
  • Python和R基因组及蛋白质组学和代谢组学
  • selenium环境搭建详细过程
  • Linux知识 - VIM
  • 【数据结构】链表重难点突破
  • 大宗商品行业区块链应用
  • Varjo:垂直起降机混合现实培训解决方案
  • sqlite-vec一个SQLite3高效向量搜索扩展--JDBC环境使用
  • 10 基于深度学习的目标检测
  • leetcode top100中的30道递归和贪心
  • 非常简单实用的前后端分离项目-仓库管理系统(Springboot+Vue)part 2
  • shell脚本(完)—脚本互调重定向的学习
  • ant-design-vue中table某一列进行合并
  • 基于Springboot+Vue社区养老服务管理系统(源码+lw+讲解部署+PPT)
  • 大数据调度组件之Apache DolphinScheduler
  • 介绍一下strlwr(arr);(c基础)
  • meterpreter常用命令 上
  • 【kubernetes】kubernetes各组件的调用关系