分析:
没见过这么暴力的数据结构题。
其实方法很简单:
首先,很容易想到一种暴力:枚举一行作为上边界,然后从左到右依次考虑每一列作右边界的情况:假设当前枚举的行为
i
i
i,列为
j
j
j,每一列
j
j
j从
i
i
i行开始,其下边的连续为1的最大长度为
l
e
n
j
len_j
lenj
很容易发现,若对于第j列,其优的左边界k必然满足:
l
e
n
k
≥
j
−
k
+
1
len_k\geq j-k+1
lenk≥j−k+1,否则的话,就可以把
k
=
k
+
1
k=k+1
k=k+1,其答案必然不会更劣。
同时,也很容易发现,最优的左边界的len一定组成了一个单调递增的序列。所以可以单调队列维护这个玩意,就可以在O(m)的复杂度内,统计完上边界在某一行的所有情况。
然后,由于询问时的矩阵限制,以及修改操作,必须对这个算法进行优化。
每一列都建一颗线段树,表示:以当前列为右边界的矩形,行的范围在[l,r]区间内,可以达到的最大空正方形。
然后,考虑如何合并两个线段:首先,两个儿子内部的不用说,只用在两侧的答案中各取一个max即可。然后就是考虑合并两个线段后新产生的正方形。
那么这个正方形一定过
m
i
d
mid
mid这一行。所以可以用O(m)的复杂度,依次从左到右考虑每一列作为右端点的情况。
没错,这就是上面的暴力。
并且,这个题线段树的合并操作是m个线段树同时合并的!
然后询问的时候就很容易控制边界矩形的影响了。询问时有个很厉害的技巧:直接搞一个空的线段出来,然后依次把它和每个划分出来的线段合并。可以很大程度上节约代码量。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 4000010
using namespace std;
int n,m;
struct matrix{
int f[MAXN*4];
int *operator [](int x){
return f+x*m;
}
}mp,rt,lf,v;
int len[MAXN*4];
void merge(int id,int idl,int idr){
static int q1[MAXN],q2[MAXN];
int hl=1,tl=0;
int hr=1,tr=0;
for(int i=1,j=1;i<=m;i++){
while(hl<=tl&&lf[idr][q1[tl]]>lf[idr][i]) tl--;
q1[++tl]=i;
while(hr<=tr&&rt[idl][q2[tr]]>rt[idl][i]) tr--;
q2[++tr]=i;
while(hl<=tl&&hr<=tr&&lf[idr][q1[hl]]+rt[idl][q2[hr]]<(i-j+1)){
while(q1[hl]<=j&&hl<=tl) hl++;
while(q2[hr]<=j&&hr<=tr) hr++;
j++;
}
v[id][i]=max(i-j+1,max(v[idl][i],v[idr][i]));
}
for(int i=1;i<=m;i++){
if(lf[idl][i]==len[idl])
lf[id][i]=len[idl]+lf[idr][i];
else
lf[id][i]=lf[idl][i];
if(rt[idr][i]==len[idr])
rt[id][i]=len[idr]+rt[idl][i];
else
rt[id][i]=rt[idr][i];
}
}
int merge(int idl,int idr,int l,int r){
static int q1[MAXN],q2[MAXN];
int hl=1,tl=0;
int hr=1,tr=0;
int res=0;
for(int i=l,j=l;i<=r;i++){
while(hl<=tl&&lf[idr][q1[tl]]>lf[idr][i]) tl--;
q1[++tl]=i;
while(hr<=tr&&rt[idl][q2[tr]]>rt[idl][i]) tr--;
q2[++tr]=i;
while(hl<=tl&&hr<=tr&&lf[idr][q1[hl]]+rt[idl][q2[hr]]<(i-j+1)){
while(q1[hl]<=j&&hl<=tl) hl++;
while(q2[hr]<=j&&hr<=tr) hr++;
j++;
}
res=max(res,i-j+1);
}
for(int i=l;i<=r;i++){
if(lf[idl][i]==len[idl])
lf[idl][i]=len[idl]+lf[idr][i];
else
lf[idl][i]=lf[idl][i];
if(rt[idr][i]==len[idr])
rt[idl][i]=len[idr]+rt[idl][i];
else
rt[idl][i]=rt[idr][i];
}
len[idl]+=len[idr];
return res;
}
void modify(int id,int l,int r,int px,int py){
if(l==r){
v[id][py]=lf[id][py]=rt[id][py]=mp[px][py];
return ;
}
int mid=(l+r)>>1;
if(px<=mid)
modify(id<<1,l,mid,px,py);
else
modify(id<<1|1,mid+1,r,px,py);
merge(id,id<<1,id<<1|1);
}
int query(int id,int l,int r,int dl,int dr,int xl,int xr){
if(dl<=l&&dr>=r){
int res=merge(0,id,xl,xr);
for(int i=xl;i<=xr;i++)
res=max(res,min(v[id][i],i-xl+1));
return res;
}
int res=0,mid=(l+r)>>1;
if(dl<=mid)
res=max(res,query(id<<1,l,mid,dl,dr,xl,xr));
if(dr>mid)
res=max(res,query(id<<1|1,mid+1,r,dl,dr,xl,xr));
return res;
}
int cquery(int dl,int dr,int xl,int xr){
for(int i=1;i<=m;i++)
v[0][i]=lf[0][i]=rt[0][i]=0;
len[0]=0;
return query(1,1,n,dl,dr,xl,xr);
}
void build(int id,int l,int r){
if(l==r){
for(int i=1;i<=m;i++)
lf[id][i]=rt[id][i]=v[id][i]=mp[l][i];
len[id]=1;
return ;
}
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
len[id]=len[id<<1]+len[id<<1|1];
merge(id,id<<1,id<<1|1);
}
int main(){
int q;
SF("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
SF("%d",&mp[i][j]);
build(1,1,n);
int tag,x,y,l,s,r,t;
for(int i=1;i<=q;i++){
SF("%d",&tag);
if(tag==0){
SF("%d%d",&x,&y);
mp[x][y]^=1;
modify(1,1,n,x,y);
}
else{
SF("%d%d%d%d",&l,&s,&r,&t);
PF("%d\n",cquery(l,r,s,t));
}
}
}