2019牛客暑期多校训练营(第三场)F - Planting Trees (枚举 + 单调队列)

题目链接

题意

有一个 N 2 N^2 N2的矩阵 N ∈ ( 1 , 500 ) N \in (1,500) N(1,500),求一个最大的子矩阵使子矩阵中最大值和最小值差不超过 M ∈ ( 0 , 1 0 5 ) M \in (0, 10^5) M(0,105),总的样例 N 3 ≤ 2.5 × 1 0 8 N^3 \le 2.5 \times 10^8 N32.5×108

思路

枚举矩阵的上下界,然后枚举右端点,同时维护两个单调队列,最大值递增,最小值递减(这样能保证如果单调队列中头元素合法之后的都可以满足 M M M的限制关系),这样就可以得到一个合法的最左边界。
复杂度: O ( N 3 ) O(N^3) O(N3)

#include 
const int maxn = 5e2 + 1;
const int inf = 0x3f3f3f3f;
using namespace std;
int a[maxn][maxn];
int up[maxn], down[maxn];
int q[maxn][2];
int main() {
	int T;
	cin >> T;
	while (T--) {
		int n, m;
		cin >> n >> m;
		for (int i = 1; i <= n; ++i) {
			for (int j = 1; j <= n; ++j) {
				cin >> a[i][j];
			}
		}
		int ans = 1;
		for (int i = 1; i <= n; ++i) { // 上界
			for (int j = 1; j <= n; ++j) {
				up[j] = -inf;
				down[j] = inf;
			}
			for (int j = i; j <= n; ++j) { // 下界
				for (int k = 1; k <= n; ++k) { // 更新列的最值
					up[k] = max(up[k], a[j][k]);
					down[k] = min(down[k], a[j][k]);
				}
				int l = 1, h0 = 0, h1 = 0, t0 = 1, t1 = 1;
				for (int r = 1; r <= n; ++r) { // 枚举右端点
					while (h0 >= t0 && down[q[h0][0]] >= down[r]) h0--; // 最大值递增
					while (h1 >= t1 && up[q[h1][1]] <= up[r]) h1--; // 最小值递减
					q[++h0][0] = q[++h1][1] = r;
					while (l <= r && up[q[t1][1]] - down[q[t0][0]] > m) { // 不满足m,左区间L++
						l++;
						while (t1 <= h1 && q[t1][1] < l) ++t1; // 更新队列头指针
						while (t0 <= h0 && q[t0][0] < l) ++t0;
					}
					ans = max(ans, (r-l+1) * (j-i+1));
				}
			}
		}
		cout << ans << endl;
	}
	return 0;
}

你可能感兴趣的:(多校,算法模板)