模板:
for (int i = 0, j = 0; i < n; i ++ )
{
while (j < i && check(i, j)) j ++ ;
// 具体问题的逻辑
}
//常见问题分类:
// (1) 对于一个序列,用两个指针维护一段区间
// (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
例题:最长连续不重复子序列
给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
第一行包含整数 n。
第二行包含 n 个整数(均在 0∼1050∼105 范围内),表示整数序列。
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
1≤n≤10^5
5
1 2 2 3 5
3
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const int N = 1e5 + 10;
using namespace std;
typedef pair PII;
int res = 0;
int a[N],s[N];
int main(){
int n;
cin >> n;
for(int i = 0;i < n;i ++)
cin >> a[i];
for(int i = 0,j = 0;i < n;i ++){
s[a[i]] ++;
while(s[a[i]] > 1){
s[a[j]] --;
j ++;
//一旦发现有某个数在s数组里面出现了两次,马上清除掉前面的记忆(也就是把前面所有的数从s数组里删掉,为了下一次求字符串的长度做准备,同时把指针j移动过来,最后的结果就是i和j移到了一起。并且i指针指的位置在s数组里还留有一个1,因为while的条件是s[a[i]]>1)
}
res = max(res,i - j + 1);
}
cout << res << endl;
return 0;
}
模板:
vector 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
}
例题:区间和
假定有一个无限长的数轴,数轴上每个坐标上的数都是 00。
现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。
接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l , r] 之间的所有数的和。
第一行包含两个整数 n 和 m。
接下来 n 行,每行包含两个整数 x 和 c。
再接下来 m 行,每行包含两个整数 l 和 r。
共 m 行,每行输出一个询问中所求的区间内数字和。
−10^9 ≤ x ≤ 10^9,
1 ≤ n , m ≤ 10^5,
−10^9 ≤ l ≤ r ≤ 10^9,
−10000 ≤ c ≤ 10000
3 3
1 2
3 6
7 5
1 3
4 6
7 8
8
0
5
代码:
#include
#include
#include
using namespace std;
const int N = 300010; //n次插入和m次查询相关数据量的上界
int n, m;
int a[N];//存储坐标插入的值
int s[N];//存储数组a的前缀和
vector alls; //存储(所有与插入和查询有关的)坐标
vector> 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() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
int x, c;
scanf("%d%d", &x, &c);
add.push_back({x, c});
alls.push_back(x);
}
for (int i = 1; i <= m; i++) {
int l , r;
scanf("%d%d", &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());
//执行前n次插入操作
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];
//处理后m次询问操作
for (auto item : query) {
int l = find(item.first);
int r = find(item.second);
printf("%d\n", s[r] - s[l-1]);
}
return 0;
}
模板:
// 将所有存在交集的区间合并
void merge(vector &segs)
{
vector 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;
}
题目:区间合并
给定 n 个区间 [l-i,r-i],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
例如:[1,3][1,3] 和 [2,6][2,6] 可以合并为一个区间 [1,6][1,6]。
第一行包含整数 n。
接下来 n 行,每行包含两个整数 l 和 r。
共一行,包含一个整数,表示合并区间完成后的区间个数。
1 ≤ n ≤ 100000,
−10^9 ≤ l-i ≤ r-i ≤ 10^9
5
1 2
2 4
5 6
7 8
7 9
3
代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
const int N = 1e5 + 10;
using namespace std;
typedef pair PII;
int n;
vector segs;
void merge(vector &segs){
vector res;
sort(segs.begin(),segs.end());
int st = -2e9, ed = -2e9; //ed代表区间结尾,st代表区间开头
for(auto seg : segs){
if(ed < seg.first){ //情况1:两个区间无法合并
if(st != -2e9) res.push_back({st,ed}); //区间1放进res数组
st = seg.first,ed = seg.second; //维护区间2
}
else ed = max(ed,seg.second); //情况2:两个区间可以合并,且区间1不包含区间2,区间2不包含区间1
}
if(st != -2e9) res.push_back({st,ed});
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;
return 0;
}