「题解」JOIOI 王国

题目描述

点这里

考场思考

因为时间不太够了,直接一上来就着手暴力。但是本人太菜,居然暴力爆 000 ,然后当场自闭…

一气之下,发现对 60pts60pts60pts 的数据范围有点思路,然后就开始码。

大概思路是 DPDPDP , 定义状态 dp[i][j]:dp[i][j]:dp[i][j]: 在第 iii 行的划分点是 jjj ,即把第 iii 行分成 [1,j][1,j][1,j] 与 [j+1,M][j+1,M][j+1,M] 。

代码大概长这样:

#include<cstdio>
#include<cstring> #define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define fep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define pii pair<int,int>
#define Endl putchar('\n')
// #define FILEOI #ifdef FILEOI
inline char fgetc(){
#define MAXSIZE 500000
static char buf[MAXSIZE+5],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXSIZE,stdin),p1==p2)?EOF:*p1++;
}
#define cg (c=fgetc())
#else
#define cg (c=getchar())
#endif
template<class T>inline void qread(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
inline int qread(){
int x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
} const int MAXN=2000;
const int MAXM=2000;
const int INF=0x3f3f3f3f; int N,M,a[MAXN+5][MAXM+5],l=INF,r,mid,ans;
int maxl[MAXN+5][MAXM+5],maxr[MAXN+5][MAXM+5];
int minl[MAXN+5][MAXM+5],minr[MAXN+5][MAXM+5];
int dp[MAXN+5][MAXM+5];
int dpmaxl[MAXN+5][MAXM+5],dpminl[MAXN+5][MAXM+5];
int dpmaxr[MAXN+5][MAXM+5],dpminr[MAXN+5][MAXM+5];
//保存极差最大值 int check(){
memset(dp,0x3f,sizeof dp);
rep(j,1,M-1){
dp[1][j]=Max(maxl[1][j]-minl[1][j],maxr[1][j+1]-minl[1][j+1]);
dpmaxl[1][j]=maxl[1][j];
dpminl[1][j]=minl[1][j];
dpmaxr[1][j]=maxr[1][j+1];
dpminr[1][j]=minr[1][j+1];
}
rep(i,2,N)rep(j,0,M)rep(k,j,M){
int pre=Max(Max(dpmaxl[i-1][k],maxl[i][j])-Min(dpminl[i-1][k],minl[i-1][j]),Max(dpmaxr[i-1][k],maxr[i][j+1])-Min(dpminr[i-1][k],minr[i-1][j+1]));
if(pre<dp[i][j]){
dp[i][j]=pre;
dpmaxl[i][j]=Max(dpmaxl[i-1][k],maxl[i][j]);
dpminl[i][j]=Min(dpminl[i-1][k],minl[i-1][j]);
dpmaxr[i][j]=Max(dpmaxr[i-1][k],maxr[i][j+1]);
dpminr[i][j]=Min(dpminr[i-1][k],minr[i-1][j+1]);
}
}
/*
rep(i,1,N){
rep(j,0,M)printf("dp[%d][%d]==%d\n",i,j,dp[i][j]);
Endl;
}
*/
int ret=INF;
rep(j,0,M)ret=Min(ret,dp[N][j]);
return ret;
} inline void init(){
qread(N,M);
rep(i,1,N)rep(j,1,M){
qread(a[i][j]);
l=Min(a[i][j],l);
r=Max(a[i][j],r);
}
rep(i,1,N){
minl[i][0]=INF;
rep(j,1,M){
maxl[i][j]=Max(maxl[i][j-1],a[i][j]);
minl[i][j]=Min(minl[i][j-1],a[i][j]);
}
}
rep(i,1,N){
minr[i][M+1]=INF;
fep(j,M,1){
maxr[i][j]=Max(maxr[i][j+1],a[i][j]);
minr[i][j]=Min(minr[i][j+1],a[i][j]);
}
}
} signed main(){
#ifdef FILEOI
freopen("file.in","r",stdin);
freopen("file.out","w",stdout);
#endif
init();
writc(check(),'\n');
return 0;
}

感觉正确性是可以保证的,但是码出来连样例一都没过,然后就自闭了…

无奈,码出来还剩 2min2min2min ,也没调试的时间了,还不如在座位上自闭一会…

正解

据 JZM\text{JZM}JZM 大佬所说,这道题他打的 二分 + DPDPDP ,这让我感到很迷茫…不过大佬就是大佬…

大概正解思路是这样的:

二分一个最优的答案 midmidmid ,这应该是可以想到的。

但是我们怎么验证这个 midmidmid 的正确性呢?如果用暴力的方法,就是枚举划分区域的样子。

但是这无疑是要超时的 而且还会 T 飞

不知道怎么做?分析一下这道题的特性:

  • 把划分的区域一排一排地看(一列一列地看也可以,看自己喜好喽…),这一定是一个单调不下降或者单调不上升序列
  • 一个区域,一定是其所包含的点越少越好(这一条仔细想想,此处不再赘述)
  • 最优的答案划分中,所有数中的最大值和最小值一定不在同一个区域中

有了这四个特性,对这道题有什么帮助呢?

其中最重要的,是第二条和第三条,那么这两条给我们怎样的启发?

一个区间,包含的东西越少越好,即每增加一个数,最好的情况就是不改变这个区域的极差。

而运气不好的话,会增加这个区域的极差。

那么对于一个区域,它其实并不想要多余的点,但是对于它对面的那个区域,其心中不也是这样想的?

那么就有一个矛盾:两边都不想要点,那怎么分?

其实,如果其中一个区域加上这个点而没有改变它的极差,那么它还是可以要这个点的。

那么,我们可以规定这个区域的极差为 midmidmid ,但是这又有一个问题:

假如说有这样一个区间:最小值为 minnminnminn ,最大值为 maxxmaxxmaxx ,且满足 maxx−minn=midmaxx-minn=midmaxx−minn=mid

那么,如果后来的某个点更改了 minnminnminn ,那么这时的 maxxmaxxmaxx 就不合法了,所以我们还要回头去把那个最大的点去掉。

无疑,这样是很麻烦的。那么我们怎么搞?

这样搞不行,那样搞也不行,我 ** 还不如不搞了…

千万不要说这样的话,心态要稳住。

这个时候,就该我们的第三点出场了:

  • 最优的答案划分中,所有数中的最大值和最小值一定不在同一个区域中

看似没用?这里就很好地规避了上面的问题:

一个区域的 最大/最小值 都被规定了,那么只需看其区域中对应的 最小/最大值 是否超过即可。

那么,我们可以规定我们 checkcheckcheck 的区间是包含最大值的,那么只需要将 ≥maxx−mid≥maxx-mid≥maxx−mid 的点尽量包含进这个区间,再看对面的区间是否满足 极差 ≤mid\le mid≤mid 即可。

代码如下:

#include<cstdio>
#include<cstring> #define rep(i,__l,__r) for(int i=__l,i##_end_=__r;i<=i##_end_;++i)
#define fep(i,__l,__r) for(int i=__l,i##_end_=__r;i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define pii pair<int,int>
#define Endl putchar('\n')
// #define FILEOI #ifdef FILEOI
inline char fgetc(){
#define MAXBUFFERSIZE 500000
static char buf[MAXBUFFERSIZE+5],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXBUFFERSIZE,stdin),p1==p2)?EOF:*p1++;
}
#undef MAXBUFFERSIZE
#define cg (c=fgetc())
#else
#define cg (c=getchar())
#endif
template<class T>inline void qread(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
inline int qread(){
int x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
template<class T>void fwrit(const T x){
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);putchar(x%10^48);
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
} const int MAXN=2000;
const int MAXM=2000;
const int INF=0x3f3f3f3f; int N,M,l=INF,r,mid,ans,maxx,minn;
int x[MAXM+5],a[MAXN+5][MAXM+5];
int premax[MAXN+5][MAXM+5],premin[MAXN+5][MAXM+5];
int sufmax[MAXN+5][MAXM+5],sufmin[MAXN+5][MAXM+5]; bool check(const int var){
int down=maxx-var;
// printf("Now down == %d, var == %d\n",down,var); // puts("-----------Case 1:-----------");
x[0]=M;
rep(i,1,N){
x[i]=0;
while(x[i]<x[i-1] && a[i][x[i]+1]>=down)++x[i];
}
// rep(i,1,N)printf("x[%d] == %d\n",i,x[i]);
int tmax=-INF,tmin=INF;
rep(i,1,N){
tmax=Max(tmax,sufmax[i][x[i]+1]);
tmin=Min(tmin,sufmin[i][x[i]+1]);
if(tmax-tmin>var)break;
}
// printf("tmax == %d, tmin == %d\n",tmax,tmin);
if(tmax-tmin<=var)return true; // puts("-----------Case 2:-----------");
x[0]=1;
rep(i,1,N){
x[i]=M+1;
while(x[i]>x[i-1] && a[i][x[i]-1]>=down)--x[i];
}
// rep(i,1,N)printf("x[%d] == %d\n",i,x[i]);
tmax=-INF,tmin=INF;
rep(i,1,N){
tmax=Max(tmax,premax[i][x[i]-1]);
tmin=Min(tmin,premin[i][x[i]-1]);
if(tmax-tmin>var)break;
}
// printf("tmax == %d, tmin == %d\n",tmax,tmin);
if(tmax-tmin<=var)return true; // puts("-----------Case 3:-----------");
x[N+1]=M;
fep(i,N,1){
x[i]=0;
while(x[i]<x[i+1] && a[i][x[i]+1]>=down)++x[i];
}
// rep(i,1,N)printf("x[%d] == %d\n",i,x[i]);
tmax=-INF,tmin=INF;
rep(i,1,N){
tmax=Max(tmax,sufmax[i][x[i]+1]);
tmin=Min(tmin,sufmin[i][x[i]+1]);
if(tmax-tmin>var)break;
}
// printf("tmax == %d, tmin == %d\n",tmax,tmin);
if(tmax-tmin<=var)return true; // puts("-----------Case 4:-----------");
x[N+1]=1;
fep(i,N,1){
x[i]=M+1;
while(x[i]>x[i+1] && a[i][x[i]-1]>=down)--x[i];
}
// rep(i,1,N)printf("x[%d] == %d\n",i,x[i]);
tmax=-INF,tmin=INF;
rep(i,1,N){
tmax=Max(tmax,premax[i][x[i]-1]);
tmin=Min(tmin,premin[i][x[i]-1]);
if(tmax-tmin>var)break;
}
// printf("tmax == %d, tmin == %d\n",tmax,tmin);
if(tmax-tmin<=var)return true; return false;
} inline void init(){
qread(N,M);
rep(i,0,N+1)rep(j,0,M+1)premin[i][j]=sufmin[i][j]=INF,premax[i][j]=sufmax[i][j]=-INF;
rep(i,1,N)rep(j,1,M){
premax[i][j]=premin[i][j]=sufmax[i][j]=sufmin[i][j]=a[i][j]=qread();
l=Min(a[i][j],l);
r=Max(a[i][j],r);
}
rep(i,1,N)rep(j,2,M){
premax[i][j]=Max(premax[i][j],premax[i][j-1]);
premin[i][j]=Min(premin[i][j],premin[i][j-1]);
}
rep(i,1,N)fep(j,M-1,1){
sufmax[i][j]=Max(sufmax[i][j],sufmax[i][j+1]);
sufmin[i][j]=Min(sufmin[i][j],sufmin[i][j+1]);
}
} inline void bisearch(){
maxx=r,minn=l;
while(l<=r){
mid=(l+r)>>1;
// printf("Now l == %d, r == %d, mid == %d, ans == %d\n",l,r,mid,ans);
if(check(mid))ans=mid,r=mid-1;
else l=mid+1;
// puts("____________________________________");
}
} signed main(){
#ifdef FILEOI
freopen("file.in","r",stdin);
freopen("file.out","w",stdout);
#endif
init();
bisearch();
writc(ans,'\n');
return 0;
}

「题解」JOIOI 王国的更多相关文章

  1. 「JOI 2017 Final」JOIOI 王国

    「JOI 2017 Final」JOIOI 王国 题目描述 题目译自 JOI 2017 Final T3「 JOIOI 王国 / The Kingdom of JOIOI」 JOIOI 王国是一个 H ...

  2. 「题解」「美团 CodeM 资格赛」跳格子

    目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...

  3. 「题解」「HNOI2013」切糕

    文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...

  4. loj#2334 「JOI 2017 Final」JOIOI 王国

    分析 二分答案 判断左上角是否满足 为了覆盖所有范围 我们依次把右下角,左上角,右上角移动到左上角 代码 #include<bits/stdc++.h> using namespace s ...

  5. 「NOIP2018」保卫王国

    「NOIP2018保卫王国」 题目描述 有一棵 \(n\) 个点, 点有点权 \(a_i\),\(m\) 组询问, 每次求钦点两个节点必须选或者必须不选后的树上最小点覆盖. \(1 \leq n, m ...

  6. 「题解」:[loj2763][JOI2013]现代豪宅

    问题 A: 现代豪宅 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...

  7. 「题解」:[POJ2942]Knights of the Round Table

    问题 E: Knights of the Round Table 时间限制: 1 Sec  内存限制: 256 MB 题面 题目描述 作为一名骑士是一个非常有吸引力的职业:寻找圣杯,拯救遇难的少女,与 ...

  8. 「题解」:$Six$

    问题 A: Six 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...

  9. 「题解」:$Smooth$

    问题 A: Smooth 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...

随机推荐

  1. Yii2.0 连接数据库

    打开数据库配置文件common\config\main-local.php

  2. TakeColor下载及调节鼠标指针不一致的问题

    取色器下载链接: 链接:https://pan.baidu.com/s/19TBWZA2ltaLQjzskTipnmg 提取码:2uz3 若出现指针与鼠标不一致: TakeColor v8.0 > ...

  3. css实现单行和多行省略号

    1.单行省略 { width:300px; overflow: hidden; text-overflow:ellipsis; whitewhite-space: nowrap; } 注:单行省略必须 ...

  4. IE浏览器清浮动

    .cfx:after,.cfx:before{content:" ";display:table;}.cfx:after{clear:both;}.cfx{*zoom:1;} 今天 ...

  5. MySQL - Schema和Database的区别

    问题来源 在pycharm发现Create new schema的效果和新建数据库一样,所以产生这个问题 参考 https://stackoverflow.com/questions/11618277 ...

  6. Ubuntu python3 与 python2 的 pip调用

    ubuntu 是默认装有pytthon2.x 与 python3.x 共存的 通常终端里 python 表示 python2 版本 python3 表示 python3 版本 (如果你没更改软链接设置 ...

  7. 2019牛客暑期多校训练营(第十场) Han Xin and His Troop (高精度+拓展中国剩余定理)

    题意 裸题 思路 题中的模数之间并不互质,所以应该用拓展中国剩余定理. 但是交上去会炸,__int128过不了,所以用高精度的板子或者java大数都挺好过的. 这里推荐java大数,因为高精度板子用起 ...

  8. 【代码审计】VAuditDemo 文件包含漏洞

    在 index.php中先判断get过来的module是否设置了变量,如果已经设置,则包含module,并与字符串.inc拼接 inc格式一般是图标或者头像格式,因此我们可以初步判断,这个包含应该是基 ...

  9. Linux终端的一些快捷键命令

    一.初识linux的终端种类:本地.远程 查看本终端命令: #tty 命令,看到当前所处的终端 #(w)who 命令,看到系统中所有登录的用户 其中,tty 终端为表示在本地命令行模式下打开的终端:p ...

  10. thinkphp 取消跳转提示

    $this->redirect('admin'); 就是直接用redirect