Luogu_P4039 【[AHOI2014/JSOI2014]拼圖】
阿新 • • 發佈:2018-12-03
1.【題目連結】
2.思路
記m = ∑^w_i
對於n <= 300的情況,暴力O(n^2)列舉矩形的上下邊界,O(m)計算矩形內的答案。
對於n > 300的情況,暴力O(n)列舉矩形的上邊界,然後列舉暴力O(m)列舉高度(最大全0子矩陣單調棧做法的高度),最後對於每個高度,O(m)計算矩形內的答案。
計算答案的過程是:
列舉每一個矩形,計算出所有矩形左邊界和右邊界能向內延伸的最大值和次大值,記錄次大值的原因是,如果左邊界右邊界最大值的矩形是同一個矩形,那麼就不合法了,就只能用次大值去更新答案。
3.Code
#include<bits/stdc++.h> //萬能標頭檔案
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++) { //for迴圈
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++) { //for迴圈
mp = g[i];
for(int j = i; j <= n; j++) { //for迴圈
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++) { //for迴圈
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() {
for(int T = iread(); T; T--) { //for迴圈
s = iread(), n = iread();
if(n <= 300) subtask1::solve();
else subtask2::solve();
}
return 0; //不寫return 0,成績return 0