参考了【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;
}