P5781 [IOI 2019] 矩形区域 题解
联考考到,误以为这道是最可做的题,然后就炸飞了。
思路
说一下一种学长介绍的思维难度相对小的做法。
我们去考虑每一个点 \((i,j)\) 作为一个矩形中的最大值的情况。如果这个最大值都比对应横纵坐标上的边框小,那么所在的行和列上的所有点都一定小于这一行或列的边框。
于是我们考虑找到 \((i,j)\) 向上下左右方向的最近的严格大于它的位置。于是我们就划定了这个矩形的边框。如果 \((i,j)\) 的确是划定矩形内的最大值,那么这个矩形的形态只可能是这样。如果不是最大值,那么这个矩形一定也会在后面被真正的最大值枚举到,于是我们去重后正确性就可以得到保证。
现在考虑如何判定划定的矩形是否是合法的。由于我们已经划定了矩形的边框,于是我们只需要判定是否边框上的数是否全部大于对应的行或列上的数。
考虑如何去判定。我们先维护对于每一个数向其上下左右最近的大于等于它的位置。然后我们以矩形的左边框为例,我们设左边框上方为 \((r1,c1)\),左边框下方为 \((r2,c1)\)(注意这里定义与原题略有不同)。那么就是判断是否有 \((r1-1,c1)\) 到 \((r2+1,c1)\) 的所有点其右方最近的大于等于它的位置都大于等于右边框。
也就是 \((r1-1,c1)\) 到 \((r2+1,c1)\) 的所有点其右方最近的大于等于它的位置的最小值要大于等于右边框,也就是一个 RMQ,写个 ST 表预处理即可。
最后统计答案记得去重。
考虑上面的复杂度瓶颈在于 ST 表预处理与去重,时空复杂度为 \(O(nm\log nm)\)。
code
我去重用的是一个 map,由于关键字的原因可能常数略大。同时由于数组很多,同时 ST 表的空间复杂度比较大,于是要开 short。
同时我没有封装,于是压了一下行。但是结构还是比较清晰的。
#include<bits/stdc++.h>
using namespace std;
const int N=2505,M=12,inf=1e9+7;
int a[N][N];short l[N][N],n,m,r[N][N],u[N][N],d[N][N],stk[N],sl[N][N][M],sr[N][N][M],su[N][N][M],sd[N][N][M],ll[N][N],rr[N][N],uu[N][N],dd[N][N];
map <pair<pair<pair<int,int>,int>,int>,int> b;
#define mp make_pair
void init(){
int top=0;
for(int i=1;i<=n;i++){
top=0;for(int j=1;j<=m;j++){while(top&&a[i][stk[top]]<a[i][j])top--;l[i][j]=stk[top];stk[++top]=j;}
top=1;a[i][m+1]=inf;stk[1]=m+1;for(int j=m;j>=1;j--){while(top&&a[i][stk[top]]<a[i][j])top--;r[i][j]=stk[top];stk[++top]=j;}
}
for(int j=1;j<=m;j++){
top=0;for(int i=1;i<=n;i++){while(top&&a[stk[top]][j]<a[i][j])top--;u[i][j]=stk[top];stk[++top]=i;}
top=1;a[n+1][j]=inf;stk[1]=n+1;for(int i=n;i>=1;i--){while(top&&a[stk[top]][j]<a[i][j])top--;d[i][j]=stk[top];stk[++top]=i;}
}
for(int i=1;i<=n;i++){
top=0;for(int j=1;j<=m;j++){while(top&&a[i][stk[top]]<=a[i][j])top--;ll[i][j]=stk[top];stk[++top]=j;}
top=1;a[i][m+1]=inf;stk[1]=m+1;for(int j=m;j>=1;j--){while(top&&a[i][stk[top]]<=a[i][j])top--;rr[i][j]=stk[top];stk[++top]=j;}
}
for(int j=1;j<=m;j++){
top=0;for(int i=1;i<=n;i++){while(top&&a[stk[top]][j]<=a[i][j])top--;uu[i][j]=stk[top];stk[++top]=i;}
top=1;a[n+1][j]=inf;stk[1]=n+1;for(int i=n;i>=1;i--){while(top&&a[stk[top]][j]<=a[i][j])top--;dd[i][j]=stk[top];stk[++top]=i;}
}
for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)sl[i][j][0]=l[i][j],sr[i][j][0]=r[i][j],su[i][j][0]=u[i][j],sd[i][j][0]=d[i][j];
for(int k=1;k<=log2(m);k++)for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(j+(1<<k)-1<=m)
su[i][j][k]=max(su[i][j][k-1],su[i][j+(1<<(k-1))][k-1]);else su[i][j][k]=max(su[i][j][k-1],su[i][m-(1<<(k-1))+1][k-1]);
for(int k=1;k<=log2(m);k++)for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(j+(1<<k)-1<=m)
sd[i][j][k]=min(sd[i][j][k-1],sd[i][j+(1<<(k-1))][k-1]);else sd[i][j][k]=min(sd[i][j][k-1],sd[i][m-(1<<(k-1))+1][k-1]);
for(int k=1;k<=log2(n);k++)for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(i+(1<<k)-1<=n)
sl[i][j][k]=max(sl[i][j][k-1],sl[i+(1<<(k-1))][j][k-1]);else sl[i][j][k]=max(sl[i][j][k-1],sl[n-(1<<(k-1))+1][j][k-1]);
for(int k=1;k<=log2(n);k++)for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(i+(1<<k)-1<=n)
sr[i][j][k]=min(sr[i][j][k-1],sr[i+(1<<(k-1))][j][k-1]);else sr[i][j][k]=min(sr[i][j][k-1],sr[n-(1<<(k-1))+1][j][k-1]);
}
int getu(int i,int L,int R){int k=log2(R-L+1);return max(su[i][L][k],su[i][R-(1<<k)+1][k]);}
int getd(int i,int L,int R){int k=log2(R-L+1);return min(sd[i][L][k],sd[i][R-(1<<k)+1][k]);}
int getl(int j,int L,int R){int k=log2(R-L+1);return max(sl[L][j][k],sl[R-(1<<k)+1][j][k]);}
int getr(int j,int L,int R){int k=log2(R-L+1);return min(sr[L][j][k],sr[R-(1<<k)+1][j][k]);}
signed main(){ //P5781
ios::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++)cin>>a[i][j];
init();int ans=0;
for(int i=2;i<n;i++)for(int j=2;j<m;j++){
int c1=ll[i][j],c2=rr[i][j],r1=uu[i][j],r2=dd[i][j];
if(c1<1||c2>m||r1<1||r2>n){continue;}
if(getr(c1,r1+1,r2-1)>=c2&&getl(c2,r1+1,r2-1)<=c1&&getu(r2,c1+1,c2-1)<=r1&&getd(r1,c1+1,c2-1)>=r2)
b[mp(mp(mp(c1,c2),r1),r2)]=1;
}
for(auto tmp:b)ans++;
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号