-
双指针算法:将时间复杂度为O(n^2)的算法转化为O(n)
例如可将如下算法:
for (int i = 0; i < n ; i ++ )
for (int j = 0; j < n; j ++)
check(i, j)
转化为如下时间复杂度为O(n)的算法
for (int i = 0; i < n; i ++ )
{
while (j < i && check(i, j))
// 执行相应操作
}
常见问题题型:
- 对于一个序列,用两个指针维护一段区间
- 对于两个序列,维护某种次序,例如归并排序将两个序列合并为一个
一个典型例题: acwing 799 最长连续不重复子序列
算法核心:将 i 从头枚举到尾, j 始终在 i 的左边 ,当j < i并且 j到i的区间内无重复元素时进行 j++,最后的停下的 j 即为 在 i 左边 的最长无重复子区间 ,并记录,比较直至循环结束
-
位运算
>> 右移运算符 :将二进制数向右移动,左边移出的空位补0或补符号位 (有符号位补符号位,无符号位补0)
<< 左移运算符:将二进制数向左移动,右边移出的空位补0
常见题型:
1) 求n的第k位数: n >> (k - 1) & 1
2) 返回n的最后一位1(最右边的一位1也就是说最低位的一位1) :lowbit(n) = n & - n
在c++中 一个数n的负数为n的取反加1(即为n的补码) 即: -n = ~n + 1 (可列举几个数进验证)
一个典型例题:acwing 801. 二进制中1的个数
核心算法:对于一个数n,在while(n) 的条件下,每次成功调用lowbit函数,记录res。并将n减去lowbit(n)
-
区间合并:将所有有交集的区间合并
算法思路:将所有区间按区间起始点大小排序,将每个区间的端点存入pair数组中,新建起始点和结尾点根据此时所在的区间进行不断更新。
例题:acwing 803 区间合并
代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int>PII;
int n;
void merge(vector<PII> &segs)
{
vector<PII>res;
sort(segs.begin(), segs.end());
int st = -2e9, ed = -2e9;
for(auto seg : segs)
{
if (seg.first > ed)
{
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});
segs = res;
}
int main()
{
cin >> n;
int l, r;
vector<PII>segs;
for (int i = 0; i < n; i ++ )
{
cin >> l >> r;
segs.push_back({l, r});
}
merge(segs);
cout << segs.size();
return 0;
}
最重要的是如何设置和更新尾端点
-
离散化
定义: 将长数轴上稀疏的点进行映射,缩小范围,从而达到减小时间复杂度
核心操作: 1)将点存入一个vector容器中,按点的下标x大小对容器进行排序(所有点的相对顺序不变)
2)根据题目的要求,容器中可能存入多个相同的点,进行去重
3)若对这些点进行一些操作利用find(x) 找到x对应于新的容器内的下标(二分)
例题: acwing 803 区间和
代码如下:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 3e6;
typedef pair<int, int> PII;
vector<int> segs;
vector<PII> args, ergs;
int arr[N], s[N];
int n, m;
int find(int x)
{
int l = 0, r = segs.size() - 1;
while(l < r)
{
int mid = (l + r) >> 1;
if (segs[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ )
{
int x, c;
scanf("%d%d", &x, &c);
segs.push_back(x);
args.push_back({x, c});
}
for (int i = 0; i < m; i ++ )
{
int l, r;
scanf("%d%d", &l, &r);
ergs.push_back({l, r});
segs.push_back(l);
segs.push_back(r);
}
sort(segs.begin(), segs.end());
segs.erase(unique(segs.begin(), segs.end()), segs.end());
for (auto arg : args)
{
int ans = find(arg.first);
arr[ans] += arg.second;
}
for (int i = 1; i <= segs.size(); i ++ ) s[i] = s[i - 1] + arr[i];
for (auto erg : ergs)
{
int l = find(erg.first), r = find(erg.second);
cout << s[r] - s[l - 1] << endl;
}
return 0;
}