「题解」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个指针,将指针对应 ...
 
随机推荐
- 让 typora和word一样好用
			
让 typora和word一样好用 :https://github.com/itcastWsy/typora_copy_images typora是一款支持实时预览的markdown编辑器,作者在使 ...
 - 安装pyhanlp
			
安装pyhanlp pyhanlp是java写的,外层封装了python. 对于新手,在使用的时候稍有难度. 1. 下载源码 https://github.com/hankcs/pyhanlp git ...
 - JS-原生的ajax
			
记录一下: //post需要设置请求头 setRequestHeader(name, value)name //头部的名称:这个参数不应该包括空白.冒号或换行 //value 头部的值:这个参数不应该 ...
 - Python - 协议和鸭子类型
			
参考: Fluent_Python - P430 wiki 这里说的协议是什么?是让Python这种动态类型语言实现多态的方式. 在面向对象编程中,协议是非正式的接口,是一组方法,但只是一种文档,语言 ...
 - Plastic Bottle Manufacturer - Different Cosmetic Plastic Bottle Materials, Different Characteristics
			
Plastic bottles are usually made of PP, PE, K, AS, abs, acrylic, PET, and the like. Dust caps for th ...
 - 实现简单ORM案例
			
ORM框架: • 我们希望设计一个可以实现对象和SQL自动映射的框架,但是整体用法和设计比Hibernate简单.砍掉不必要的功能.• 会穿插使用设计模式• 增加 – 将对象对应成sql语句,执行sq ...
 - vjudge Trailing Zeroes (III) (二分答案 && 数论)
			
嗯... 题目链接:https://vjudge.net/contest/318956#problem/E 这道题是二分答案+数论,但首先是数论,否则你不知如何二分... 首先关于一个阶乘的结果最后会 ...
 - mcast_block_source函数
			
#include <errno.h> #include <sys/socket.h> #define SA struct sockaddr int mcast_block_so ...
 - DHT协议网络爬虫磁力链接和BT种子搜索引擎
			
系统功能和用到的技术. 系统包括几个独立的部分: 使用 Python 的 Scrapy 框架开发的网络爬虫,用来爬取磁力链接和种子: 使用 PHP CI 框架开发的简易网站: 搜索引擎目前直接使用的 ...
 - iOS项目开发日常之创建文件(协议、类、分类、扩展)
			
iOS项目开发过程中,是以不断创建文件的形式进行着的. 创建得比较频繁的文件类型是: 这两个类型中创建的文件有:子类.分类.扩展.协议四种文件,如下: 这四类文件是频繁创建的,我们来看一下各自分 ...