题目大意:
给一个(n)个元素的序列,从中挑出最长的子串,要求子串中元素差的最大值不超过(k)。问有几个最长子串,子串长度,以及这几个子串的起始、终止位置。
思路:
很容易想到尺取法。
我们使用(multiset)来完成对维护尺取的区间,因为(multiset)具有有序性和可充分性,而且在默认的情况下,该容器内的元素是使(<)运算符比较大小,也就是容器内部按升序排列。所以我们可以通过(crbegin())和(cbegin())分别取出容器内的最大值和最小值进行模拟。
值得一提的是,在模拟单调队列的弹出操作时,我们使用
[MuiltisetName.erase(MuiltisetName.find(value))
]
来删除指定值。因为如果直接删除指定值的话,(erase)函数会将所有与指定值相同的元素删去,就不符合题意了。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100010;
const int INF = 0x3f3f3f3f;
inline int read(){
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main() {
int n = read(), k = read();
vector<pair<int, int> > p;
multiset<int> s;
vector<int> h(N);
int l = 0, max_len = 0;
for (int r = 0; r < n; r++) {
h[r] = read();
s.insert(h[r]);
while (*s.crbegin() - *s.cbegin() > k)
s.erase(s.find(h[l++]));
if (r - l + 1 > max_len) {
p.clear(); //要记录最长序列长度的位置
max_len = r - l + 1;
p.push_back(make_pair(l + 1, r + 1));
} else if (r - l + 1 == max_len) {
p.push_back(make_pair(l + 1, r + 1));
}
}
printf("%d %d
", max_len, p.size());
for (auto i : p) {
printf("%d %d
", i.first, i.second);
}
return 0;
}