Atcoder 2444 - JOIOI 王国(二分)

题面传送门

\(mxi\) 为 IOI 国海拔的最大值,\(mni\) 为 IOI 国海拔的最小值,\(mxj\) 为 JOI 国海拔的最大值,\(mnj\) 为 JOI 国海拔的最小值。
不难发现 \(mxi,mni,mxj,mnj\) 中有 2 个值已经确定下来了,\(\max(mxi,mxj)\) 一定等于矩阵的全局最大值 \(mx\)\(\min(mni,mnj)\) 一定等于矩阵的全局最小值 \(mn\)
如果我们把海拔最高和最低的点分配到了同一个国家中,答案即为 \(mx-mn\)
如果我们把海拔最高和最低的点分配到了不同的国家中,我们不妨假设海拔最高的点分配到了 JOI 国,海拔最低的点分配到了 IOI 国。
二分答案。
假设二分到 \(mid\),那么所有 IOI 国的城市的海拔 \(\leq mn+mid\),所有 JOI 国的城市的海拔 \(\geq mx-mid\)
也就是所有海拔 \(>mn+mid\) 的城市全部属于 JOI 国,所有海拔 \(<mx-mid\) 的城市全部属于 IOI 国。
此时题目转化为:已知某些点属于 IOI 国,某些点属于 JOI 国,判断是否存在一种合法的分配方案。
根据题意两国的地形一定呈阶梯分部。所以可以分出四种情况,这里以 JOI 国占据左上角,IOI 国占据右下角为例。
考虑第 \(i\) 两国之间的分界线 \(b_i\),那么一定有 \(b_i \leq b_{i-1}\),而第 \(i\)\(b_i\) 左边肯定都是 JOI 国的城市,第 \(i\) 行右边肯定都是 IOI 国的城市,根据这个你可以求出 \(b_i\) 的最大值和最小值,然后判断是否有交集即可。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e3+5;
int n,m,a[MAXN][MAXN],mx=0,mn=0x3f3f3f3f;
int l[MAXN],r[MAXN];
bool check(int mid){
	memset(l,0,sizeof(l));memset(r,0,sizeof(r));r[0]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]>mn+mid) l[i]=max(l[i],j);
	for(int i=n;i;i--) l[i]=max(l[i],l[i+1]);
	for(int i=1;i<=n;i++) r[i]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]<mx-mid) r[i]=min(r[i],j);
	for(int i=1;i<=n;i++) r[i]=min(r[i-1],r[i]);
	bool flg=1;for(int i=1;i<=n;i++) flg&=(l[i]<r[i]);if(flg) return 1;
	memset(l,0,sizeof(l));memset(r,0,sizeof(r));r[n+1]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]>mn+mid) l[i]=max(l[i],j);
	for(int i=1;i<=n;i++) l[i]=max(l[i],l[i-1]);
	for(int i=1;i<=n;i++) r[i]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]<mx-mid) r[i]=min(r[i],j);
	for(int i=n;i;i--) r[i]=min(r[i+1],r[i]);
	flg=1;for(int i=1;i<=n;i++) flg&=(l[i]<r[i]);if(flg) return 1;
	memset(l,0,sizeof(l));memset(r,0,sizeof(r));r[0]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]<mx-mid) l[i]=max(l[i],j);
	for(int i=n;i;i--) l[i]=max(l[i],l[i+1]);
	for(int i=1;i<=n;i++) r[i]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]>mn+mid) r[i]=min(r[i],j);
	for(int i=1;i<=n;i++) r[i]=min(r[i-1],r[i]);
	flg=1;for(int i=1;i<=n;i++) flg&=(l[i]<r[i]);if(flg) return 1;
	memset(l,0,sizeof(l));memset(r,0,sizeof(r));r[n+1]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]<mx-mid) l[i]=max(l[i],j);
	for(int i=1;i<=n;i++) l[i]=max(l[i],l[i-1]);
	for(int i=1;i<=n;i++) r[i]=m+1;
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(a[i][j]>mn+mid) r[i]=min(r[i],j);
	for(int i=n;i;i--) r[i]=min(r[i+1],r[i]);
	flg=1;for(int i=1;i<=n;i++) flg&=(l[i]<r[i]);if(flg) return 1;
	return 0;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) mx=max(mx,a[i][j]),mn=min(mn,a[i][j]);
	int l=0,r=mx-mn-1,ans=mx-mn;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	} printf("%d\n",ans);
	return 0;
}
posted @ 2020-12-03 14:33  tzc_wk  阅读(85)  评论(0编辑  收藏  举报