「题解」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 王国的更多相关文章
- 「JOI 2017 Final」JOIOI 王国
「JOI 2017 Final」JOIOI 王国 题目描述 题目译自 JOI 2017 Final T3「 JOIOI 王国 / The Kingdom of JOIOI」 JOIOI 王国是一个 H ...
- 「题解」「美团 CodeM 资格赛」跳格子
目录 「题解」「美团 CodeM 资格赛」跳格子 题目描述 考场思路 思路分析及正解代码 「题解」「美团 CodeM 资格赛」跳格子 今天真的考自闭了... \(T1\) 花了 \(2h\) 都没有搞 ...
- 「题解」「HNOI2013」切糕
文章目录 「题解」「HNOI2013」切糕 题目描述 思路分析及代码 题目分析 题解及代码 「题解」「HNOI2013」切糕 题目描述 点这里 思路分析及代码 题目分析 这道题的题目可以说得上是史上最 ...
- loj#2334 「JOI 2017 Final」JOIOI 王国
分析 二分答案 判断左上角是否满足 为了覆盖所有范围 我们依次把右下角,左上角,右上角移动到左上角 代码 #include<bits/stdc++.h> using namespace s ...
- 「NOIP2018」保卫王国
「NOIP2018保卫王国」 题目描述 有一棵 \(n\) 个点, 点有点权 \(a_i\),\(m\) 组询问, 每次求钦点两个节点必须选或者必须不选后的树上最小点覆盖. \(1 \leq n, m ...
- 「题解」:[loj2763][JOI2013]现代豪宅
问题 A: 现代豪宅 时间限制: 1 Sec 内存限制: 256 MB 题面 题目描述 (题目译自 $JOI 2013 Final T3$「現代的な屋敷」) 你在某个很大的豪宅里迷路了.这个豪宅由东 ...
- 「题解」:[POJ2942]Knights of the Round Table
问题 E: Knights of the Round Table 时间限制: 1 Sec 内存限制: 256 MB 题面 题目描述 作为一名骑士是一个非常有吸引力的职业:寻找圣杯,拯救遇难的少女,与 ...
- 「题解」:$Six$
问题 A: Six 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 来写一篇正经的题解. 每一个数对于答案的贡献与数本身无关,只与它包含了哪几个质因数有关. 所以考虑二 ...
- 「题解」:$Smooth$
问题 A: Smooth 时间限制: 1 Sec 内存限制: 512 MB 题面 题面谢绝公开. 题解 维护一个队列,开15个指针,对应前15个素数. 对于每一次添加数字,暴扫15个指针,将指针对应 ...
随机推荐
- 《MySQL命令执行过程和存储引擎概述》阅读笔记
使用MySQL的完整过程: 启动MySQL服务器程序. 启动MySQL客户端程序并连接到服务器程序. 在客户端程序中输入一些命令语句发送到服务器程序,服务器程序收到这些请求后,会根据请求的内容来操作具 ...
- 「SDOI2009」虔诚的墓主人
传送门 Luogu 解题思路 离散化没什么好说 有一种暴力的想法就是枚举每一个坟墓,用一些数据结构维护一下这个店向左,向右,向上,向下的常青树的个数,然后用组合数统计方案. 但是网格图边长就有 \(1 ...
- 10.Redis的RDB和AOF两种持久化机制的优劣势对比
1.RDB和AOF两种持久化机制的介绍 2.RDB持久化机制的优点3.RDB持久化机制的缺点4.AOF持久化机制的优点5.AOF持久化机制的缺点6.RDB和AOF到底该如何选择 我们已经知道对于一个企 ...
- Navicat for MySQL怎么往表中填数据
只有往表中更新数据,数据库才会起到真正的作用. 工具/原料 仔细阅读 方法/步骤 1.打开数据库,首先连接localhost,如图所示. 2.连接成功后,右侧便会显示已经建成的表,找到要修改的表, ...
- 解决游览器安装Vue.js devtools插件无效的问题
一: 打开自己写的一个vue.js网页,发现这个图标并没有亮起来,还是灰色 解决方案: 1.我们先看看Vue.js devtools是否生效,打开Bilibili(https://www.bilib ...
- git合并分支到master上面
转自:https://www.cnblogs.com/mafeng/p/10173919.html 假如我们现在在dev分支上,刚开发完项目,执行了下列命令 git add .git commit - ...
- ASA设置某些log不发送到log server
If you want to suppress a specific syslog message to be sent to syslog server, then you must enter t ...
- Cisco AP-AP重置操作
Resetting to Default Settings Using the MODE Button/spanFollow these steps to reset the access point ...
- 设计模式课程 设计模式精讲 3-4 依赖倒置原则讲解+coding
1 课程讲解 1.1 定义 1.2 优点 1.3 细节描述 2 代码演练 2.0 代码展示优点 2.1 非面向接口编程 2.2 面向接口编程1 传参 2.3 面向接口编程2 构造函数 2.4 面向接口 ...
- typeof方法重写(区分数组对象)
为什么要重写typeof方法? typeof 可以准确的判断除object以外的基础数据类型,但不能区分object类型的具体类型,比如 Array .Date.NULL.NaN 以及自定义类. 观察 ...