Link
显然答案具有单调性,所以我们考虑二分答案。
由题可知,JOI和IOI的分界线是单调的,即要么是从左上到右下,要么是从左下到右上。
并且我们知道,最大值和最小值肯定不能在同一个省里。
所以要么是最大值(mx)在左边的省里,要么是最小值(mn)在左边的省里。
所以对于一个二分出来答案(lim),我们需要满足(mx)所在的省中的最小值大于等于(mx-lim),(mn)所在省中的最大值小于等于(mn+lim)。
所以我们枚举最大值的两种情况(一种在左一种在右),再枚举分界线的两种情况。总共四种情况,有一种情况可以满足要求,那么这个(lim)就是可以满足的。
现在考虑如何check。
比如对于最大值在左边,分界线从左上到右下的情况。这种情况左边的最小值必须大于等于(mx-lim)。
我们(O(n^2))贪心地求出每一行最多能往右扩展到哪里。
然后再从下往上,计算每一行的边界线(下面一行的分界线和这一样最多能扩展到地方的最小值)。
这样边界线以左的部分显然会满足最小值大于等于(mx-lim)并且边界线单调。
所以我们只需要(O(n))检查右边是否存在大于(mn+lim)的数即可。
总体而言,我们只需要预处理出每一行前缀后缀的最大值最小值即可。
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
int read(){int x=0;char c=getchar();while(!isdigit(c))c=getchar();while(isdigit(c))x=x*10+c-48,c=getchar();return x;}
int min(int a,int b){return a<b? a:b;}
int max(int a,int b){return a>b? a:b;}
const int N=2007,inf=1e9+7;
int t[N],a[N][N],pmx[N][N],smx[N][N],pmn[N][N],smn[N][N],n,m,lim,mn=inf,mx=-inf,mxlim,mnlim;
void mncal(){for(int i=1,j;i<=n;++i)for(t[i]=0,j=1;j<=m;++j)if(pmx[i][j]<=mnlim)t[i]=j;else break;}
void mxcal(){for(int i=1,j;i<=n;++i)for(t[i]=0,j=1;j<=m;++j)if(pmn[i][j]>=mxlim)t[i]=j;else break;}
int c1(){mncal();for(int i=n,p=m;i;--i){p=min(p,t[i]);if(smn[i][p+1]<mxlim) return 0;}return 1;}
int c2(){mxcal();for(int i=n,p=m;i;--i){p=min(p,t[i]);if(smx[i][p+1]>mnlim) return 0;}return 1;}
int c3(){mncal();for(int i=1,p=m;i<=n;++i){p=min(p,t[i]);if(smn[i][p+1]<mxlim) return 0;}return 1;}
int c4(){mxcal();for(int i=1,p=m;i<=n;++i){p=min(p,t[i]);if(smx[i][p+1]>mnlim) return 0;}return 1;}
int main()
{
n=read(),m=read();int i,j,l,r,ans;
for(i=1;i<=n;smn[i][m+1]=pmn[i][0]=inf,++i) for(j=1;j<=m;++j) a[i][j]=read(),mx=max(mx,a[i][j]),mn=min(mn,a[i][j]);
for(i=1;i<=n;++i)
{
for(j=1;j<=m;++j) pmx[i][j]=max(a[i][j],pmx[i][j-1]),pmn[i][j]=min(a[i][j],pmn[i][j-1]);
for(j=m;j;--j) smx[i][j]=max(a[i][j],smx[i][j+1]),smn[i][j]=min(a[i][j],smn[i][j+1]);
}
for(l=0,r=mx-mn;l<=r;) lim=mid,mxlim=mx-lim,mnlim=mn+lim,(c1()||c2()||c3()||c4()? (ans=mid,r=mid-1):(l=mid+1));
printf("%d
",ans);
}