6488. 【GDOI2020模拟02.29】土地勘测land
题目描述
题解
每次可以找一维折半,所以答案不超2log
反着做,变成每次把两个相邻的区间合并
枚举答案,维护f[i][j][k]表示当前第i行j~k列最多能向下到哪一行,g[i][j][k]表示列
每次先自己转移自己,然后考虑用另一个数组转移
比如对于当前的f[i][j][k],可以转到满足g[j][i][l]>=k的l(g已经转移过自己),画图理解
g同理
而当一个矩形包含另一个矩形时,被包含的矩形的操作次数必然不增,同理在某一条边界上缩一格时也不增
因此可以单调维护l
其中自己转移自己之后可以直接用回老数组,具体感性理解
关于转移顺序与时间: https://www.cnblogs.com/gmh77/p/12392514.html
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define ll long long
#define file
using namespace std;
bool a[221][221];
int sum[221][221],f[222][221][221],g[222][221][221],n,m,i,j,k,l,ans;
char ch;
bool pd(int x1,int y1,int x2,int y2)
{
int s=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
return !s || s==(x2-x1+1)*(y2-y1+1);
}
void init()
{
fo(j,1,m)
{
fo(k,j,m)
{
fo(i,1,n)
{
f[i][j][k]=max(f[i-1][j][k],i-1);
while (f[i][j][k]<n && pd(i,j,f[i][j][k]+1,k))
++f[i][j][k];
}
}
}
fo(j,1,n)
{
fo(k,j,n)
{
fo(i,1,m)
{
g[i][j][k]=max(g[i-1][j][k],i-1);
while (g[i][j][k]<m && pd(j,i,k,g[i][j][k]+1))
++g[i][j][k];
}
}
}
}
int main()
{
freopen("land.in","r",stdin);
#ifdef file
freopen("land.out","w",stdout);
#endif
scanf("%d%d",&n,&m);
fo(i,1,n)
{
fo(j,1,m)
{
ch=getchar();
while (ch!='.' && ch!='#')
ch=getchar();
a[i][j]=ch=='#';
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
}
}
fo(i,1,m){fo(j,i,m)f[n+1][i][j]=n;}
fo(i,1,n){fo(j,i,n)g[m+1][i][j]=m;}
init();
while (!(f[1][1][m]==n || g[1][1][n]==m))
{
++ans;
fo(j,1,m)
{
fo(k,j,m)
{
l=0;
fo(i,1,n)
{
l=max(l,i-1);
while (l<n && g[g[j][i][l+1]+1][i][l+1]>=k)
++l;
f[i][j][k]=max(f[f[i][j][k]+1][j][k],l);
}
}
}
fo(j,1,n)
{
fo(k,j,n)
{
l=0;
fo(i,1,m)
{
l=max(l,i-1);
while (l<m && f[j][i][l+1]>=k)
++l;
g[i][j][k]=max(g[g[i][j][k]+1][j][k],l);
}
}
}
}
printf("%d\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}