前言
今天开到一道题,luogu1169棋盘制作,发现自己不会,问了一下同学,告诉我说是普及组题,感觉自己枉为提高组选手。。。
然后学习了一波悬线法。
以
表示(i,j)这个点向左走碰到的第一个障碍,
表示向右走碰到的第一个障碍。
即所谓的悬线,表示向上走最多能走几步,那么有一个显然的结论,最后的这个答案,一定是一条悬线向左右两边扩展得到,不然的话完全可以再向上走一步。
表示我(i,j)的这条悬线向左边碰到的第一个障碍,
表示向右边碰到的第一个障碍,那么我最后的这个答案就是(
1)
。
求法可能比较难以说明,直接上代码吧,非常清楚
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<iostream>
#define LL long long
#define INF (2139062143)
#define N (2005)
using namespace std;
int n,m,x,ans1,t,ans;
int map[N][N],l[N][N],r[N][N],L[N][N],R[N][N],a[N][N],h[N][N];
template <typename T> void read(T&t) {
t=0;
bool fl=true;
char p=getchar();
while (!isdigit(p)) {
if (p=='-') fl=false;
p=getchar();
}
do {
(t*=10)+=p-48;p=getchar();
}while (isdigit(p));
if (!fl) t=-t;
}
int main(){
read(n),read(m);
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
read(x);
a[i][j]=x^((i+j)&1);
}
}
for (int i=1;i<=n;i++){
t=0;
for (int j=1;j<=m;j++){
if (a[i][j]){
l[i][j]=t;
}
else t=j,L[i][j]=0;
}
t=m+1;
for (int j=m;j>1;j--){
if (a[i][j]){
r[i][j]=t;
}
else t=j,R[i][j]=m+1;
}
}
for (int i=1;i<=m;i++) L[0][i]=0,R[0][i]=m+1;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
if (a[i][j]){
h[i][j]=h[i-1][j]+1;
L[i][j]=max(L[i-1][j],l[i][j]);
R[i][j]=min(R[i-1][j],r[i][j]);
ans=max(ans,(R[i][j]-L[i][j]-1)*h[i][j]);
int tt=min(R[i][j]-L[i][j]-1,h[i][j]);
ans1=max(ans1,tt*tt);
}
else h[i][j]=0;
}
}
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
memset(L,0,sizeof(L));
memset(R,0,sizeof(R));
memset(h,0,sizeof(h));
for (int i=1;i<=n;i++){
t=0;
for (int j=1;j<=m;j++){
if (!a[i][j]){
l[i][j]=t;
}
else t=j,L[i][j]=0;
}
t=m+1;
for (int j=m;j>1;j--){
if (!a[i][j]){
r[i][j]=t;
}
else t=j,R[i][j]=m+1;
}
}
for (int i=1;i<=m;i++) L[0][i]=0,R[0][i]=m+1;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
if (!a[i][j]){
h[i][j]=h[i-1][j]+1;
L[i][j]=max(L[i-1][j],l[i][j]);
R[i][j]=min(R[i-1][j],r[i][j]);
ans=max(ans,(R[i][j]-L[i][j]-1)*h[i][j]);
int tt=min(R[i][j]-L[i][j]-1,h[i][j]);
ans1=max(ans1,tt*tt);
}
else h[i][j]=0;
}
}
printf("%d\n%d",ans1,ans);
return 0;
}