Codeforces Round #248 (Div. 1) B. Nanami's Digital Board(DP+ 双指针/单调栈)

B. Nanami's Digital Board
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Nanami is an expert at playing games. This day, Nanami's good friend Hajime invited her to watch a game of baseball. Unwilling as she was, she followed him to the stadium. But Nanami had no interest in the game, so she looked around to see if there was something that might interest her. That's when she saw the digital board at one end of the stadium.

The digital board is n pixels in height and m pixels in width, every pixel is either light or dark. The pixels are described by its coordinate. The j-th pixel of the i-th line is pixel (i, j). The board displays messages by switching a combination of pixels to light, and the rest to dark. Nanami notices that the state of the pixels on the board changes from time to time. At certain times, certain pixels on the board may switch from light to dark, or from dark to light.

Nanami wonders, what is the area of the biggest light block such that a specific pixel is on its side. A light block is a sub-rectangle of the board, in which all pixels are light. Pixel (i, j) belongs to a side of sub-rectangle with (x1, y1) and (x2, y2) as its upper-left and lower-right vertex if and only if it satisfies the logical condition:

(( i = x1 or  i = x2) and ( y1 ≤ j ≤ y2)) or (( j = y1 or  j = y2) and ( x1 ≤ i ≤ x2)).

Nanami has all the history of changing pixels, also she has some questions of the described type, can you answer them?

Input

The first line contains three space-separated integers nm and q (1 ≤ n, m, q ≤ 1000) — the height and width of the digital board, and the number of operations.

Then follow n lines, each line containing m space-separated integers. The j-th integer of the i-th line is ai, j — the initial state of pixel (i, j).

  • If ai, j = 0, pixel (i, j) is initially dark.
  • If ai, j = 1, pixel (i, j) is initially light.

Then follow q lines, each line containing three space-separated integers opx, and y (1 ≤ op ≤ 2; 1 ≤ x ≤ n; 1 ≤ y ≤ m), describing an operation.

  • If op = 1, the pixel at (x, y) changes its state (from light to dark or from dark to light).
  • If op = 2, Nanami queries the biggest light block with pixel (x, y) on its side.
Output

For each query, print a single line containing one integer — the answer to Nanami's query.

Sample test(s)
input
3 4 5
0 1 1 0
1 0 0 1
0 1 1 0
2 2 2
2 1 2
1 2 2
1 2 3
2 2 2
output
0
2
6
input
3 3 4
1 1 1
1 1 1
1 1 1
2 2 2
1 2 2
2 1 1
2 2 1
output
6
3
3
Note

Consider the first sample.

The first query specifies pixel (2, 2), which is dark itself, so there are no valid light blocks, thus the answer is 0.

The second query specifies pixel (1, 2). The biggest light block is the block with (1, 2) as its upper-left vertex and (1, 3) as its lower-right vertex.

The last query specifies pixel (2, 2), which became light in the third operation. The biggest light block is the block with (1, 2) as its upper-left vertex and (3, 3) as its lower-right vertex.



题意:

已知一01矩阵,有两个操作。

1.使(x,y)的值反转(0变成1,1变成0)

2.找出一个最大的1矩阵且(x,y)点必须在这个矩阵边界上


思路:这种最大子矩阵的显然要用经典的dp,维护每个点向上向下向左向右连续的1的最大距离

然后可以用往常的最大子矩阵的思路用单调栈dp出以每个高度往左往右延伸的最远距离,然后只要跨过(x,y)点就符合

但这种用单调栈的方法很经典但是不方便

也可以用双指针,比如从(x,y)有左右移动的两个双指针,每次根据max(h[l-1],h[r+1])决定向左还是向右移动,然后一直维护目前为止h(igh)数组的最小值,更新ans=max(ans,curminh*len)即可,这种双指针的移动规则一定能遍历最优解




法一(单调栈):

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>

using namespace std;

const int N = 1e3+100;
int mp[N][N];
int up[N][N],dwn[N][N],lft[N][N],rgt[N][N];

int n,m,q;
void update(int x,int y)
{
    mp[x][y]^=1;
    for(int i=x;i<=n;i++) up[i][y]=mp[i][y]? up[i-1][y]+1:0;
    for(int i=x;i>=1;i--) dwn[i][y]=mp[i][y]? dwn[i+1][y]+1:0;
    for(int j=y;j<=m;j++) lft[x][j]=mp[x][j]? lft[x][j-1]+1:0;
    for(int j=y;j>=1;j--) rgt[x][j]=mp[x][j]? rgt[x][j+1]+1:0;
}
const int inf = 0x3f3f3f3f;
int ll[N],rr[N];
int sta[N],top;
int h[N];
int getmax(int L,int R,int p)
{
    int ans=0;
    sta[0]=L-1;top=0;
    for(int i=L;i<=R;i++)
    {
        while(top&&h[sta[top]]>=h[i]) top--;
        ll[i]=sta[top];
        sta[++top]=i;
    }
    sta[0]=R+1;top=0;
    for(int i=R;i>=L;i--)
    {
        while(top&&h[sta[top]]>=h[i]) top--;
        rr[i]=sta[top];
        sta[++top]=i;
    }
    for(int i=L;i<=R;i++)
        if(rr[i]-1>=p&&ll[i]+1<=p)
            ans=max(ans,(rr[i]-ll[i]-1)*h[i]);
    return ans;
}
int query(int x,int y)
{
    int ans=0;
    int l=lft[x][y],u=up[x][y],d=dwn[x][y],r=rgt[x][y];
    int L=y-l+1,R=y+r-1;
    for(int i=L;i<=R;i++) h[i]=up[x][i];
    ans=max(ans,getmax(L,R,y));
    for(int i=L;i<=R;i++) h[i]=dwn[x][i];
    ans=max(ans,getmax(L,R,y));
    L=x-u+1,R=x+d-1;
    for(int i=L;i<=R;i++) h[i]=lft[i][y];
    ans=max(ans,getmax(L,R,x));
    for(int i=L;i<=R;i++) h[i]=rgt[i][y];
    ans=max(ans,getmax(L,R,x));
    return ans;
}
int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]);
    for(int y=1;y<=m;y++)
    {
        for(int x=1;x<=n;x++) up[x][y]=mp[x][y]? up[x-1][y]+ 1:0;
        for(int x=n;x>=1;x--) dwn[x][y]=mp[x][y]? dwn[x+1][y]+ 1:0;
    }
    for(int x=1;x<=n;x++)
    {
        for(int y=1;y<=m;y++) lft[x][y]=mp[x][y]? lft[x][y-1]+1:0;
        for(int y=m;y>=1;y--) rgt[x][y]=mp[x][y]? rgt[x][y+1]+1:0;
    }
    for(int i=1;i<=q;i++)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            update(x,y);
        }
        else
        {
            int x,y;scanf("%d%d",&x,&y);
            int ans=query(x,y);
            cout<<ans<<endl;
        }
    }
    return 0;
}

法二(双指针):

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>

using namespace std;

const int N = 1e3+100;
int mp[N][N];
int up[N][N],dwn[N][N],lft[N][N],rgt[N][N];

int n,m,q;
void update(int x,int y)
{
    mp[x][y]^=1;
    for(int i=x;i<=n;i++) up[i][y]=mp[i][y]? up[i-1][y]+1:0;
    for(int i=x;i>=1;i--) dwn[i][y]=mp[i][y]? dwn[i+1][y]+1:0;
    for(int j=y;j<=m;j++) lft[x][j]=mp[x][j]? lft[x][j-1]+1:0;
    for(int j=y;j>=1;j--) rgt[x][j]=mp[x][j]? rgt[x][j+1]+1:0;
}
const int inf = 0x3f3f3f3f;
int ll[N],rr[N];
int sta[N],top;
int h[N];
int getmax(int L,int R,int p)
{
    int ans=h[p];
    int curmin=h[p];
    int curl=p,curr=p;
    while(true)
    {
        if(curl==L&&curr==R) break;
        int a=-1,b=-1;
        if(curl-1>=L) a=h[curl-1];
        if(curr+1<=R) b=h[curr+1];
        if(a>b) curl--;else curr++;
        curmin=min(curmin,max(a,b));
        ans=max(ans,(curr-curl+1)*curmin);
    }
    return ans;
}
int query(int x,int y)
{
    int ans=0;
    int l=lft[x][y],u=up[x][y],d=dwn[x][y],r=rgt[x][y];
    int L=y-l+1,R=y+r-1;
    for(int i=L;i<=R;i++) h[i]=up[x][i];
    if(L<=R) ans=max(ans,getmax(L,R,y));
    for(int i=L;i<=R;i++) h[i]=dwn[x][i];
    if(L<=R) ans=max(ans,getmax(L,R,y));
    L=x-u+1,R=x+d-1;
    for(int i=L;i<=R;i++) h[i]=lft[i][y];
    if(L<=R) ans=max(ans,getmax(L,R,x));
    for(int i=L;i<=R;i++) h[i]=rgt[i][y];
    if(L<=R) ans=max(ans,getmax(L,R,x));
    return ans;
}
int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]);
    for(int y=1;y<=m;y++)
    {
        for(int x=1;x<=n;x++) up[x][y]=mp[x][y]? up[x-1][y]+ 1:0;
        for(int x=n;x>=1;x--) dwn[x][y]=mp[x][y]? dwn[x+1][y]+ 1:0;
    }
    for(int x=1;x<=n;x++)
    {
        for(int y=1;y<=m;y++) lft[x][y]=mp[x][y]? lft[x][y-1]+1:0;
        for(int y=m;y>=1;y--) rgt[x][y]=mp[x][y]? rgt[x][y+1]+1:0;
    }
    for(int i=1;i<=q;i++)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            update(x,y);
        }
        else
        {
            int x,y;scanf("%d%d",&x,&y);
            int ans=query(x,y);
            cout<<ans<<endl;
        }
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值