A.完美子区间
题面
给定 $ n, A, B $ 和序列 $ a_n $,求最长的满足以下条件的区间的长度:
\[ 1\leq l < r \leq n \\A \leq \max \{ a_l, a_{l + 1}, ..., a_r \} - \min \{ a_l, a_{l + 1}, ..., a_r \} \leq B
\]
思路
不难发现对于一个 $ l $ ,合法的 $ r $ 显然是一个区间。
不合法的 $ r $ 有两种情况:
-
在合法区间的左边。这种情况下 $ (l, r) $ 这一区间的极差只能小于 $ A $。
-
在合法区间的右边。这种情况下 $ (l, r) $ 这一区间的极差只能大于 $ B $。
P.S. 注意到如果没有合法区间的话 $ (l + 1, n) $ 可以分成两部分,一段是极差小于 $ A $ ,另一段是极差大于 $ B $。
所以最大的合法 $ r $ 单调,可以二分。
代码
#include<bits/stdc++.h>
//#define int long long
#define Debug puts("Oops!")
using namespace std;const int N = 5e5 + 5, M = 5e5 + 5;int n, x, y;
int a[N], l[N], r[N];
int f[N][20], g[N][20];int getmin(int l, int r) {int b = log2(r - l + 1);return min(f[l][b], f[r - (1 << b) + 1][b]);
}int getmax(int l, int r) {int b = log2(r - l + 1);return max(g[l][b], g[r - (1 << b) + 1][b]);
}
inline int read() {int x = 0, f = 1; char c = getchar();while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}while(isdigit(c)) x = x * 10 + c - '0', c = getchar();return x * f;
}signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);while(scanf("%d", &n) != EOF) {for(int i = 1; i <= n; i++)for(int j = 0; j <= 19; j++)f[i][j] = 0x3f3f3f3f, g[i][j] = 0;x = read(), y = read();for(int i = 1; i <= n; i++) a[i] = read(), f[i][0] = g[i][0] = a[i];for(int j = 1; j <= 19; j++) {for(int i = 1; i <= n; i++)f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]),g[i][j] = max(g[i][j - 1], g[i + (1 << j - 1)][j - 1]);}int res = 0;for(int i = 1; i <= n; i++) {int l = i, r = n;while(l <= r) {int mid = l + r >> 1;if(getmax(i, mid) - getmin(i, mid) > y) r = mid - 1;else if(getmax(i, mid) - getmin(i, mid) < x) l = mid + 1;else l = mid + 1, res = max(res, mid - i + 1);}}printf("%d\n", res);}return 0;
}
C.相互看见
题面
给定 $ n $ 和 数列 $ a_n $。求满足以下条件的数对 $ (i, j) $个数。
\[\max\{ a_{i+1}, a_{i+2}, ..., a_{j-1} \} \leq \min ( a_i, a_j )
\]
P.S. 相邻的两个数也算
思路
不难发现某个点 $ i $ 右侧合法的 $ j $ 一定是单调递增的。
而第一个合法的 $ j $ 就是 $ i + 1 $,最后一个合法的 $ j $ 就是第一个 $ a_j > a_i $。
很容易发现这其实就是单调栈弹栈的过程,所以直接单调栈中计数即可。
细节看代码注释。
代码
#include<bits/stdc++.h>
#define int long long
#define Debug puts("Oops!")
using namespace std;const int N = 5e5 + 5, M = 5e5 + 5;int n, a[N];inline int read() {int x = 0, f = 1; char c = getchar();while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}while(isdigit(c)) x = x * 10 + c - '0', c = getchar();return x * f;
}signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);n = read();for(int i = 1; i <= n; i++) a[i] = read();stack<pair<int, int> > st;int res = 0;//栈中相同的一段数比较难处理,直接把它们压成一个东西即可。for(int i = 1; i <= n; i++) {int tmp = 1;while(!st.empty() && a[st.top().first] <= a[i]) {res += st.top().second;if(!st.empty() && a[st.top().first] == a[i]) tmp += st.top().second;st.pop();}if(!st.empty()) res++;st.push({i, tmp});}cout << res << endl;return 0;
}