【AtCoder Regular Contest 081 F】Flip and Rectangles

结论题,单调栈。

题目大意:

给定 $H\times W$ 的棋盘,每个位置为黑色或白色。

你可以任意执行以下操作:

  • 选择一行或一列,将这上面的所有格子的颜色翻转。

问进行操作后,棋盘上的最大全黑子矩形的面积最大是多少。

题解:

这题有一个结论:对于任意 $2\times 2$ 的子矩形,它能被染成全黑,当且仅当其中恰好有偶数个格子为白色。

我们发现,在这个结论下,共用格子是不会产生冲突的。也就是说所有 $2\times 2$ 的子矩形都可以独立考虑。

那么我们将可能染黑的 $2\times 2$ 的格子找出来(记录左上角),然后找它的面积最大的子矩形即可。

这个可以用单调栈在 $O(H\times W)$ 里求出。

这样求出的答案的两条边长度至少为 $2$,所以还有一条边长度为 $1$ 的情况。此时最大方案为 $\max(H,W)$。答案取 $\max$ 即可。

Code:

#include<cstdio>
#include<algorithm>
using std::max;
const int N=2333;
int pre[N][N],L[N],R[N];
int n,m,h,t,top;
char mp[N][N];
int sta[N];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%s",mp[i]+1);
    for(int i=1;i<n;++i){
        for(int j=1;j<m;++j){
            int x=(mp[i][j]=='.')+(mp[i][j+1]=='.')+(mp[i+1][j]=='.')+(mp[i+1][j+1]=='.');
            if(x%2==0)pre[j][i]=pre[j-1][i]+1;else pre[j][i]=0;
        }
    }
    int ans=n;
    if(ans<m)ans=m;
    for(int j=1;j<m;++j){
        int*P=pre[j];
        sta[top=1]=L[1]=1;
        *sta=0;
        for(int i=2;i<n;++i){
            while(top&&P[i]<=P[sta[top]])--top;
            L[i]=sta[top]+1;
            sta[++top]=i;
        }
        sta[top=1]=R[n-1]=n-1;
        *sta=n;
        for(int i=n-2;i;--i){
            while(top&&P[i]<=P[sta[top]])--top;
            R[i]=sta[top]-1;
            sta[++top]=i;
        }
        for(int i=1;i<n;++i)ans=max(ans,(R[i]-L[i]+2)*(P[i]+1));
    }
    printf("%d\n",ans);
    return 0;
}