P4147题解
题解区全是悬线法,但是看到 Um_nik 听了会很开心的
这个思路是我在某学校打模拟赛时想出来的,下面我们来讲讲这个神奇思路。
我们知道,一个矩形可以用左上角的坐标和右下角的坐标表示,这是因为这两个点可以确定唯一一条直线,也就是矩形的对角线。显然有一个性质:本题中如果表示左上角的 F 的矩形,那么
那么我们怎么判定是否能组成一个全是 F 的矩形呢?直接扫一遍肯定不行,但是可以二维前缀和。记 F 为 R 为
我们有了上面这些,就能写出代码了,但注意不能写成这样:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1005][1005],f[1005][1005],res=0;
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
char ch;
cin>>ch;
if(ch=='F')a[i][j]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]=a[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int l=1,r=n,ans=0;
while(l<=r){
int mid=(l+r)>>1;
int ll=1,rr=m,ans2=-1;
while(ll<=rr){
int mmid=(ll+rr)>>1;
if((f[mid][mmid]-f[i-1][mmid]-f[mid][j-1]+f[i-1][j-1])==(mid-i+1)*(mmid-j+1))ans2=mmid,ll=mmid+1;
else rr=mmid-1;
}
if(ans2!=-1)ans=(mid-i+1)*(ans2-j+1),l=mid+1;
else r=mid-1;
}
res=max(res,ans);
}
cout<<res*3;
}
那么,为什么不能这样写呢?考虑一种情况。
F F F F F F F
F F F F F F F
F F F F F F F
F F R R R R R
F F R R R R R
F F R R R R R
F F R R R R R
F F R R R R R
可以看到,我们上面的代码直接在左上角坐标 R。至此,我们已经确定了一个 break 掉,原理同上。另外,当取得的矩形面积已经等于 F 的个数了,也就没必要继续算了。
二分一定要注意边界情况,
代码非常好写:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1005][1005],f[1005][1005],res=0;
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
char ch;
cin>>ch;
if(ch=='F')a[i][j]=1;//F视为1
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j]=a[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1];//前缀和预处理
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
for(int k=i;k<=n;k++){
if((k-i+1)*(m-j+1)<=res)continue;//多剪掉一个情况
int ll=j,rr=m,ans=-1;
while(ll<=rr){//二分
int mmid=(ll+rr)>>1;
if((f[k][mmid]-f[i-1][mmid]-f[k][j-1]+f[i-1][j-1])==(k-i+1)*(mmid-j+1))ans=(k-i+1)*(mmid-j+1),ll=mmid+1;//判断是否满足要求
else rr=mmid-1;
}
res=max(res,ans);
if(ans==-1)break;
if(ans==f[n][m]){
cout<<ans*3;
return 0;
}
//去掉部分冗余计算
}
}
cout<<res*3;
}
最慢的点跑了 288ms,可以通过。