题目链接
题意就是有一个长度为n的数组A,询问A中有多少个子段,满足子段中的最大值为X且最小值为Y。
1
≤
n
≤
2
e
5
1 \leq n \leq 2e5
1≤n≤2e5
分析:
首先我们考虑暴力解法,显然时间复杂度是
O
(
n
3
)
O(n^3)
O(n3)的,加上数据结构优化之后为
O
(
n
2
l
o
g
n
)
O(n^2log_n)
O(n2logn),显然无法通过此题,考虑其他的方法。
观察之后可以发现,有一些区间是无论如何也不会成为答案的,这些区间的特征是包含大于X的数或者包含小于Y的数。
我们可以将这些数称为P,(
p
≤
Y
∣
∣
X
≤
p
p \leq Y || X \leq p
p≤Y∣∣X≤p),那么我们就可以将这些数作为分割点,将这些可行的区间划分开来。例如样例:A = (4,2,5,4,3,4,2),X = 4, Y = 2。
划分后就为:A = (4,2)(4,3,4,2)
对于这些被划分的区间,显然其中的数值只可能Y到X之间,那么其实我们的问题就变成了——寻找既包含X又包含Y的区间数。
这个问题可以用尺取法解决,因为其存在单调性:
例如我们现在有一个被分割后的区间 [ L , R ] [L,R] [L,R],设 ( L ≤ j ≤ k ≤ R ) (L \leq j \leq k \leq R) (L≤j≤k≤R),如果区间 [ j , k ] [j,k] [j,k]即包含X又包含Y,那么显然区间 [ L , R ] [L,R] [L,R]也满足既包含X又包含Y。
AC代码:
#include<bits/stdc++.h>
#define endl '\n'
#define IO ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
const int N = 3e5 + 10, M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int a[N], idx = 1;
vector<int> b[N];
/*
滑动窗口(双指针)算法所说的单调性指的是区间与子区间之间是否存在单调性,例如本题中,
[i,j]是[l,r]的一个子区间,即有l <= i <= j <= r,如果[i,j]满足包含x和y,那么显然[l,r]也满足包含x和y
这就是所谓的单调性!
*/
void solve() {
int n, x, y;
cin >> n >> x >> y;
for(int i = 1; i <= n; ++ i) cin >> a[i];
for(int i = 1; i <= n; ++ i) {
if((a[i] < y || a[i] > x)) {
if(b[idx].size() != 0) idx ++;
}
else b[idx].push_back(a[i]);
}
ll ans = 0;
for(int k = 1; k <= idx; ++ k) {
map<int, int> mp;
for(int i = 0, j = 0; i < b[k].size(); ++ i) {
while((mp[x] == 0 || mp[y] == 0) && j < b[k].size()) {
mp[b[k][j]] ++;
j ++;
}
if(mp[x] != 0 && mp[y] != 0) ans = ans + (int)b[k].size() - j + 1; //如果都存在的话,显然右端点不断往后移也一定存在(对于不同的左端点统计答案)。
mp[b[k][i]] --;
}
cout << endl;
}
cout << ans << endl;
}
int main() {
IO;
// int t;
// cin >> t;
// while(t --)
solve();
return 0;
}
/*
1 2
1 2 3 6 3 2 4 1
1 2
*/