【BZOJ3873】[Ahoi2014]拼图【分类讨论】【暴力】

【题目链接】

参考了【wuyuhan的题解】

记m = ∑wi

对于n <= 300的情况,暴力O(n^2)枚举矩形的上下边界,O(m)计算矩形内的答案。

对于n > 300的情况,暴力O(n)枚举矩形的上边界,然后枚举暴力O(m)枚举高度(最大全0子矩阵单调栈做法的高度),最后对于每个高度,O(m)计算矩形内的答案。

计算答案的过程是:

枚举每一个矩形,计算出所有矩形左边界和右边界能向内延伸的最大值和次大值,记录次大值的原因是,如果左边界右边界最大值的矩形是同一个矩形,那么就不合法了,就只能用次大值去更新答案。

/* Telekinetic Forest Guard */
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>

using namespace std;

const int maxn = 305, maxm = 100005;

int s, n, m, pos[maxm];
bitset<maxm> g[maxn], mp;

inline int iread() {
	int f = 1, x = 0; char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) f = ch == '-' ? -1 : 1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return f * x;
}

inline int cread() {
	char ch = getchar(); for(; ch != '0' && ch != '1'; ch = getchar());
	return ch - '0';
}

namespace subtask1 {
	void solve() {
		int ans = 0;
		for(int i = 1; i <= s; i++) {
			pos[i] = pos[i - 1] + iread();
			for(int j = 1; j <= n; j++) for(int k = pos[i - 1] + 1; k <= pos[i]; k++)
				g[j][k] = cread();
		}
		m = pos[s];
		for(int i = 1; i <= n; i++) {
			mp = g[i];
			for(int j = i; j <= n; j++) {
				mp |= g[j];
				for(int k = 1, sum = 0; k <= m; k++) {
					sum = mp[k] ? 0 : sum + 1;
					ans = max(ans, sum * (j - i + 1));
				}
				int l1 = 0, l2 = 0, lno = 0;
				int r1 = 0, r2 = 0, rno = 0;
				int sum = 0, full = 0;
				for(int k = 1; k <= s; k++) {
					sum = 0;
					for(int l = pos[k - 1] + 1; l <= pos[k]; l++, sum++)
						if(mp[l]) break;
					if(sum == pos[k] - pos[k - 1]) {
						full += sum;
						continue;
					}
					if(sum > l1) l2 = l1, l1 = sum, lno = k;
					else if(sum > l2) l2 = sum;
					sum = 0;
					for(int r = pos[k]; r >= pos[k - 1] + 1; r--, sum++)
						if(mp[r]) break;
					if(sum > r1) r2 = r1, r1 = sum, rno = k;
					else if(sum > r2) r2 = sum;
				}
				ans = max(ans, (((lno == rno) ? max(l1 + r2, l2 + r1) : l1 + r1) + full) * (j - i + 1));
			}
		}
		printf("%d\n", ans);
	}
}

namespace subtask2 {
	int f[maxm];

	void solve() {
		int ans = 0;
		for(int i = 1; i <= s; i++) {
			pos[i] = pos[i - 1] + iread();
			for(int k = 1; k <= n; k++) for(int j = pos[i - 1] + 1; j <= pos[i]; j++)
				g[j][k] = cread();
		}
		m = pos[s];
		for(int i = n; i; i--) {
			for(int j = 1; j <= m; j++) f[j] = g[j][i] ? 0 : f[j] + 1;
			for(int j = 1; j <= m; j++) {
				for(int k = 1, sum = 0; k <= m; k++) {
					sum = f[k] < f[j] ? 0 : sum + 1;
					ans = max(ans, f[j] * sum);
				}
				int l1 = 0, l2 = 0, lno = 0;
				int r1 = 0, r2 = 0, rno = 0;
				int sum = 0, full = 0;
				for(int k = 1; k <= s; k++) {
					sum = 0;
					for(int l = pos[k - 1] + 1; l <= pos[k]; l++, sum++)
						if(f[l] < f[j]) break;
					if(sum == pos[k] - pos[k - 1]) {
						full += sum;
						continue;
					}
					if(sum > l1) l2 = l1, l1 = sum, lno = k;
					else if(sum > l2) l2 = sum;
					sum = 0;
					for(int r = pos[k]; r >= pos[k - 1] + 1; r--, sum++)
						if(f[r] < f[j]) break;
					if(sum > r1) r2 = r1, r1 = sum, rno = k;
					else if(sum > r2) r2 = sum;
				}
				ans = max(ans, (((lno == rno ? max(l1 + r2, l2 + r1) : l1 + r1) + full) * f[j]));
			}
		}
		printf("%d\n", ans);
	}
}

int main() {
	freopen("puzzle.in", "r", stdin);
	freopen("puzzle.out", "w", stdout);
	for(int T = iread(); T; T--) {
		s = iread(), n = iread();
		if(n <= 300) subtask1::solve();
		else subtask2::solve();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值