[HAOI2007]理想的正方形

传送


UPD.2021.03.09
这道题有两种做法,一种是单调队列,另一种是二维st表。


单调队列的做法就是枚举上边界\(i\),这样就能确定矩形的下边界为\(i+r-1\)。在这个范围内,用每一个竖条的最大值(最小值)代替整个竖条,就变成了滑动窗口问题了。


代码里存了原矩阵和元素为相反数的矩阵,这样只用调用同一个函数两遍就能求最大值和最小值了,看起来简单些。
时间复杂度\(O(n^2r)\),虽然可以预处理出\(n\)个st表在\(O(1)\)时间内求竖条最值,使复杂度达到\(O(n^2logn+n^2)\),但没别要.

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const int maxn = 1e3 + 5;
In ll read()
{
	ll ans = 0;
	char ch = getchar(), las = ' ';
	while(!isdigit(ch)) las = ch, ch = getchar();
	while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
	if(las == '-') ans = -ans;
	return ans;
}
In void write(ll x)
{
	if(x < 0) x = -x, putchar('-');
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}

int n, m, r;
#define pr pair<int, int>
#define mp make_pair
#define F first
#define S second 
struct Node
{
	int a[maxn][maxn];
	deque<pr> q;
	In void calc(int x, int y)
	{
		int Max = -INF;
		for(int i = 0; i < r; ++i) Max = max(Max, a[x + i][y]);
		while(!q.empty() && q.back().F < Max) q.pop_back();
		while(!q.empty() && q.front().S <= y - r) q.pop_front();
		q.push_back(mp(Max, y));
	}
}Ma, Mi;

In int solve(int x)
{
	Ma.q.clear(), Mi.q.clear();
	int ret = INF;
	for(int i = 1; i <= m; ++i)
	{
		Ma.calc(x, i), Mi.calc(x, i);
		if(i >= r) ret = min(ret, Ma.q.front().F + Mi.q.front().F);
	}
	return ret;
}

int main()
{
	n = read(), m = read(), r = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j) Ma.a[i][j] = read(), Mi.a[i][j] = -Ma.a[i][j];
	int ans = INF;
	for(int i = 1; i <= n - r + 1; ++i) ans = min(ans, solve(i));
	write(ans), enter;
	return 0;
}


另一种做法是二维st表。 预处理的时候,一个大矩形的最值由四个小矩形的最值合并而来。 查询的时候,在$n*n$的矩阵中找到四个最大且边长不找过$n$的$2^k$的矩形。

然后就可以了,时间复杂度\(O(n^2logn+n^2)\).

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<stack>
#include<queue>
#include<vector>
#include<cctype>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a) memset(a, 0, sizeof(a))
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e3 + 5;
inline ll read()
{
	ll ans = 0;
	char ch = getchar(), last = ' ';
	while(!isdigit(ch)) {last = ch; ch = getchar();}
	while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
	if(last == '-') ans = -ans;
	return ans;
}
inline void write(ll x)
{
	if(x < 0) putchar('-'), x = -x;
	if(x >= 10) write(x / 10);
	putchar(x % 10 + '0');
}

int x, y, n, a[maxn][maxn];

int Max[maxn][maxn][7], Min[maxn][maxn][7], nk;
void rmq()
{
	for(int i = 1; i <= x; ++i)
		for(int j = 1; j <= y; ++j) Max[i][j][0] = Min[i][j][0] = a[i][j];
	for(int k = 1; (1 << k) <= n; ++k)
		for(int i = 1; i + (1 << k) - 1 <= x; ++i)
			for(int j = 1; j + (1 << k) - 1 <= y; ++j)
			{
				Max[i][j][k] = max(max(Max[i][j][k - 1], Max[i][j + (1 << (k - 1))][k - 1]), max(Max[i + (1 << (k - 1))][j][k - 1], Max[i + (1 << (k - 1))][j + (1 << (k - 1))][k - 1]));
				Min[i][j][k] = min(min(Min[i][j][k - 1], Min[i][j + (1 << (k - 1))][k - 1]), min(Min[i + (1 << (k - 1))][j][k - 1], Min[i + (1 << (k - 1))][j + (1 << (k - 1))][k - 1]));
			}
	while((1 << (nk + 1)) <= n) nk++;
}

int ans = 2147483647;

int main()
{
	x = read(); y = read(); n = read();
	for(int i = 1; i <= x; ++i)
		for(int j = 1; j <= y; ++j) a[i][j] = read();
	rmq();
	for(int i = 1; i <= x - n + 1; ++i)
		for(int j = 1; j <= y - n + 1; ++j)
		{
			int _max = max(max(Max[i][j][nk], Max[i][j + n - (1 << nk)][nk]), max(Max[i + n - (1 << nk)][j][nk], Max[i + n - (1 << nk)][j + n - (1 << nk)][nk]));
			int _min = min(min(Min[i][j][nk], Min[i][j + n - (1 << nk)][nk]), min(Min[i + n - (1 << nk)][j][nk], Min[i + n - (1 << nk)][j + n - (1 << nk)][nk]));
			ans = min(ans, _max - _min);
		}
	write(ans); enter;
	return 0;
}
posted @ 2018-08-21 16:19  mrclr  阅读(133)  评论(0编辑  收藏  举报