bzoj1057 [ZJOI2007]棋盘制作

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1057

【题解】

把网格图黑白染色,把原来的颜色异或黑白染色的颜色,就变成求最大0/1子矩形/正方形

以最大全1子矩形为例。

我们设a[i,j]表示第i行第j个之前有多少个连续的1。

那么我们维护a单调上升的单调栈

每次加入一个元素,如果比前面大等直接做,小的话更新答案,(这个元素到前面那个元素的一大段都能达到前面那个元素的a值)

注意更新完答案,要更新位置值,这个值为这次更新最后一个出栈的那个位置。

(因为我可以连续更新到那里啊。。这一大段都行)

所以就行了。

# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 2e3 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, m, ans1, ans2;
int mp[M][M], a[M][M];
int stn;
struct pa {
    int x, h; 
    pa() {}
    pa(int x, int h) : x(x), h(h) {}
}st[M];

inline int sqr(int x) {
    return x*x;
}

inline void doit(int pos, int H) {
    int pre = pos;
    while(stn && st[stn].h > H) {
        ans1 = max(ans1, (pos-st[stn].x)*st[stn].h);
        ans2 = max(ans2, sqr(min(pos-st[stn].x, st[stn].h)));
        pre = st[stn].x;
        --stn;
    }
    st[++stn] = pa(pre, H);
}

inline void getans() {
    for (int i=1; i<=n; ++i) 
        for (int j=1; j<=m; ++j) 
            if(mp[i][j] == 0) a[i][j] = 0;
            else a[i][j] = a[i][j-1] + 1; 
    for (int i=1; i<=m; ++i) {
        stn = 0;
        for (int j=1; j<=n; ++j)
            doit(j, a[j][i]);
        doit(n+1, 0);
    }
}

int main() {
    cin >> n >> m;
    for (int i=1; i<=n; ++i) 
        for (int j=1; j<=m; ++j) { 
            scanf("%d", &mp[i][j]);
            int c = (i+j)&1;
            mp[i][j] ^= c; 
        }
    
//    for (int i=1; i<=n; ++i, cout << endl) 
//        for (int j=1; j<=m; ++j) cout << mp[i][j];  

    getans();
    
    for (int i=1; i<=n; ++i) 
        for (int j=1; j<=m; ++j) 
            mp[i][j] ^= 1; 
    
    getans(); 
    
    cout << ans2 << endl << ans1 << endl;
    
    return 0;
}
View Code

 

posted @ 2017-05-24 21:37  Galaxies  阅读(174)  评论(0编辑  收藏  举报