位运算模板 + 区间合并

193 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

位运算

常用操作

  1. n的二进制表示中的第k位的数是几
    1. 注意:个位是第0位,一次类推
    2. 基本思路
      1. 先将第k位的数移到最后一位:n>>k
      2. 看一下个位是几:x & 1
      3. 结合上面的公式得到:n >> k & 1
    3. 例子:输出n的二进制表示
      1. image.png
      2. image.png
  2. lowbit(x):返回x的最后一位1(也就是最右边一位)
    1. 它是树状数组的一个基本操作
    2. 返回的是一个二进制数
    3. 例如: image.png
    4. 怎么实现的?
      1. x& -x = x & (~x + 1)
      2. 在c++里面,-x = ~x + 1:-x = x取反+1
    5. 验证:为什么x&-x能够做到返回最后一位1呢?
      1. image.png
    6. 应用:统计一下x里面有多少个1
      1. 做法:每次将x里的最后一个1去掉
      2. 当x=0的时候,里面就没有1了,减了多少次,说明里面有多少个1

题目

www.acwing.com/problem/con… image.png

思路分析如下:

实际上就是一个lowbit的操作

代码

#include <iostream>

using namespace std;

int lowbit(int x)
{
    return x & -x;
}

int main()
{
    int n; // n个数
    cin >> n;
    while (n--)
    {
        int x; 
        cin >> x; // 读入x
        
        // 统计一下x里面1的个数
        int res = 0;
        // 当x不是0的时候,每次做如下操作
        while (x) x -= lowbit(x), res ++;  // 每次减去x的最后一个1  
    	
        cout << res << ' ';
    }
    
    return 0;
}

区间合并

应用场景

image.png

  1. 有多个区间,让我们将有交集的区间进行合并,合并成一个新的区间
  2. 区间合并算法:快速的将有交集的区间进行合并

题目

image.png

解题步骤

image.png

  1. 将所有区间的左端点排序
  2. 扫描整个区间,在这个过程中,我们将所有可能有交集的区间进行合并
    1. 怎么做?
      1. 每次维护当前的区间(左端点:st,右端点:ed )
      2. 从从前往后扫描的过程中,假设扫描到了第i个区间,那么这个区间跟当前维护的区间存在以下三种关系,以及对应情况是如何更新的:
        1. 在当前维护区间的内部:区间范围不变
        2. 有交集但是不在内部:区间变长
        3. 没有交集 :从这个区间开始都跟我现在维护的区间没有交集了,所以当前维护的区间就可以放到答案里面了,并将当前维护的区间更新成现在扫描到的第i个区间

代码

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

typedef pair<int, int> PII;

const int N = 100010;

int n; // 有n个区间,将n个区间存到vector里面
vector<PII> segs;

void merge(vector<PII> &segs)
{ 
    vector<PII> res; // 答案:合并之后的结果
    
    // 1.对所有区间进行排序
    sort(segs.begin(), segs.end());
    
    // 从前往后扫描,并维护当前的区间,
    int st = -2e9, ed = -2e9; // 定义区间(-∞ ~ -∞),一开始是没有需要维护的区间的
    // 从前往后扫描所有线段
    for (auto seg : segs)
        // 情况一:没有交集
        if (ed < seg.first)
        {
            if (st != -2e9) res.push_back({st, ed}); // 将维护的区间放到答案中
            st = seg.first, ed = seg.second; // 更新一下维护的区间:变成了下一个
             
        }
    	// 情况二:有交集
    	else ed = max(ed, seg.second);
    
    // 将最后一个区间加入到答案里面
    if (st != -2e9) res.push_back({st, ed});
    
    // 将区间更新成res
    segs = res;
}

int main()
{
    cin >> n;
    
    for (int i = 0; i < n; i++)
    {
        int l, r;
        cin >> l >> r; // 读入每个区间的左右端点
        segs.push_back({l, r});
    }
    
    // 区间合并的模板
    merge(segs);
    
    // 返回合并之后的区间的个数
    
    cout << segs.size() << endl;  
}

总结

  1. 跟双指针算法不一样,因为合并区间只有一个指针