一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
位运算
常用操作
- n的二进制表示中的第k位的数是几
- 注意:个位是第0位,一次类推
基本思路:- 先将第k位的数移到最后一位:
n>>k - 看一下个位是几:
x & 1 - 结合上面的公式得到:
n >> k & 1
- 先将第k位的数移到最后一位:
- 例子:输出n的二进制表示
lowbit(x):返回x的最后一位1(也就是最右边一位)- 它是树状数组的一个基本操作
- 返回的是一个二进制数
- 例如:
- 怎么实现的?
x& -x = x & (~x + 1)- 在c++里面,
-x = ~x + 1:-x = x取反+1
- 验证:为什么
x&-x能够做到返回最后一位1呢? - 应用:统计一下x里面有多少个1
- 做法:每次将x里的最后一个1去掉
- 当x=0的时候,里面就没有1了,减了多少次,说明里面有多少个1
题目
思路分析如下:
实际上就是一个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;
}
区间合并
应用场景
- 有多个区间,让我们将有交集的区间进行合并,合并成一个新的区间
- 区间合并算法:快速的将有交集的区间进行合并
题目
解题步骤
- 将所有区间的左端点排序
- 扫描整个区间,在这个过程中,我们将所有可能有交集的区间进行合并
- 怎么做?
- 每次维护当前的区间(左端点:st,右端点:ed )
- 从从前往后扫描的过程中,假设扫描到了第i个区间,那么这个区间跟当前维护的区间存在以下三种关系,以及对应情况是如何更新的:
- 在当前维护区间的内部:区间范围不变
- 有交集但是不在内部:区间变长
- 没有交集 :从这个区间开始都跟我现在维护的区间没有交集了,所以当前维护的区间就可以放到答案里面了,并将当前维护的区间更新成现在扫描到的第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;
}
总结
- 跟双指针算法不一样,因为合并区间只有一个指针