1. 程式人生 > >Luogu_P4039 【[AHOI2014/JSOI2014]拼圖】

Luogu_P4039 【[AHOI2014/JSOI2014]拼圖】

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