码迷,mamicode.com
首页 > 其他好文 > 详细

JOI2017FinalC JOIOI 王国

时间:2020-02-21 17:44:48      阅读:63      评论:0      收藏:0      [点我收藏+]

标签:处理   test   现在   oid   turn   部分   答案   code   include   

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\n",ans);
}

JOI2017FinalC JOIOI 王国

标签:处理   test   现在   oid   turn   部分   答案   code   include   

原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12342222.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!