康复训练赛

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 $ 有两种情况:

  1. 在合法区间的左边。这种情况下 $ (l, r) $ 这一区间的极差只能小于 $ A $。

  2. 在合法区间的右边。这种情况下 $ (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;
}
posted @ 2024-12-21 14:46  zeta炀  阅读(9)  评论(0)    收藏  举报