1 双指针算法(滑动窗口)
1.1 模板
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
}
常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
1.2 习题1 —— 799.最长连续不重复序列
Acwing 799.最长连续不重复子序列
- 红色指针和绿色指针中间是没有重复数字的,绿色指针是 j,红色指针是 i。判断红绿色指针之间有没有重复数字。红色指针向后移动的话,绿色指针也一定会向后移动,它们之间具有单调性。
- 如果判断之间有重复元素的话,就 j++。
- 这里应该是i - j +1, 不是j - i + 1
- 使用s[N]来保存a[N]所对应数字的数量,无论是a[i]还是a[j]都会找到相同的对应数字
#include<iostream>
using namespace std;
const int N = 100010;
int n;
int a[N];
int s[N];
int main()
{
cin >> n;
for(int i = 0;i < n; i++) cin >> a[i];
int res = 0;
for(int i = 0, j = 0;i < n;i++)
{
s[a[i]]++;
while(s[a[i]] > 1)
{
s[a[j]] --;
j ++;
}
res = max(res, i - j + 1);
}
cout << res << endl;
return 0;
}
1.3 习题2 —— 800.数组元素的目标和
Acwing 800.数组元素的目标和
1.4 习题3 —— 816.判断子序列
Acwing 816.判断子序列
2 位运算
2.1 模板
求n的第k位数字: n >> k & 1
返回n的最后一位1:lowbit(n) = n & -n
2.1.1 求n的第k位数字
2.1.2 返回n的最后一位1(lowbit函数)
返回n的最后一位1:lowbit(n) = n & -n,相当于lowbit(n) = n & (~n + 1) 取反+1
- lowbit函数
- 具体实现步骤
2.2 习题1 —— 801.二进制中1的个数(lowbit函数)
Acwing 801.二进制中1的个数
#include<iostream>
using namespace std;
int lowbit(int x)
{
return x & -x;
}
int main()
{
int n;
cin >> n;
while(n--)
{
int x;
cin >> x;
int res = 0;
while(x) x -= lowbit(x), res++;
cout << res << ' ';
}
return 0;
}
- 源码、补码、反码
3 离散化(重要!)
3.1 模板
- erase的地方是重复的元素
- unique函数会返回不重复的前面那段最后数字的下标
vector<int> alls;
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
int find(int 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;
}
3.2 习题1 —— 802.区间和
Acwing 802.区间和
- 特点是数字非常稀疏,需要用离散化方式做
- 在x位置加上c,只需要a[x] += c
- 做n次操作,需要n个下标,m次询问,需要2m个下标。所以总共只需要2m+n个下标
- 把用到数组下标映射到从1开始的自然数
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int,int> PII;
const int N = 300010;
int a[N], s[N];
vector<int> alls;
vector<PII> add, query;
int find(int 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;
}
int main()
{
int n, m;
cin >> n >> m;
for(int i = 0; i < n; i++)
{
int x, c;
cin >> x >> c;
add.push_back({x,c});
alls.push_back(x);
}
for(int i = 0; i < m; i++)
{
int l, r;
cin >> l >> r;
query.push_back({l, r});
alls.push_back(l);
alls.push_back(r);
}
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
for(auto item : add)
{
int x = find(item.first);
a[x] += item.second;
}
for(int i = 1; i <= alls.size(); i++) s[i] = s[i - 1]+ a[i];
for(auto item : query)
{
int l = find(item.first), r = find(item.second);
cout << s[r] - s[l-1] << endl;
}
return 0;
}
4 区间合并
4.1 模板
void merge(vector<PII> &segs)
{
vector<PII> res;
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});
segs = res;
}
4.2 习题1 —— 803.区间合并
Acwing 803.区间合并
- 三种情况分析
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 100010;
vector<PII> segs;
void merge(vector<PII>&segs)
{
vector<PII> res;
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});
segs = res;
}
int main()
{
int n;
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;
return 0;
}