题目大意
给定一个序列,求有多少个区间满足区间最大值减区间最小值大于k。
解题思路
可以从反面入手来解决这个问题,就是把答案变成区间个数减去区间最大值减最小值小于等于k的区间个数,求后者我们可以通过枚举区间的右端点找区间左端点有多少种可能的情况,然后累加起来就行了。我们可以发现如果区间 满足题意那么区间
也满足题意,所以我们只要对每一个
求出最左边的
使
满足题意,那么右端点为
的满足条件的区间一共有
个,累加起来即为答案。当然不可能暴力求
,题目也不允许二分来求。通观察可以发现每一个
对应的
是有单调性的,即
移动到
,
是不会向左移动的,这样我们就可以由
对应的
向右移动来得到
对应的
,非常类似于双指针。
(其实就是双指针) 由于最多移动
次,所以算法复杂度
,符合题目要求。
代码实现
至于如何在O(1)时间内求出区间最大值和最小值,那当然要用单调队列来维护了。具体细节请参考代码:
#include <bits/stdc++.h> using namespace std; deque<tuple<int, int>> mi, ma; const int MAXN = 1e7 + 5; const int Mod = 1e9; int n, k, a0, b, c; int a[MAXN]; int main() { cin >> n >> k >> a[0] >> b >> c; for (int i = 1; i <= n; i++) a[i] = ((long long)a[i - 1] * b + c) % Mod; int l = 1; long long ans = (long long)n * (n + 1) / 2; for (int r = 1; r <= n; r++) { while (!mi.empty() && get<0>(mi.back()) > a[r]) mi.pop_back(); while (!ma.empty() && get<0>(ma.back()) < a[r]) ma.pop_back(); mi.push_back(make_tuple(a[r], r)); ma.push_back(make_tuple(a[r], r)); while (get<0>(ma.front()) - get<0>(mi.front()) > k) { l++; while (get<1>(mi.front()) < l) mi.pop_front(); while (get<1>(ma.front()) < l) ma.pop_front(); } ans -= (r - l + 1); } cout << ans << endl; return 0; }