• Atcoder 2444


    题面传送门

    (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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    准备 FRM 考试——方法、工具与教训
    930. 和相同的二元子数组 前缀和
    1906. 查询差绝对值的最小值 前缀和
    剑指 Offer 37. 序列化二叉树 二叉树 字符串
    815. 公交路线 BFS
    518. 零钱兑换 II dp 完全背包
    1049. 最后一块石头的重量 II dp
    5779. 装包裹的最小浪费空间 二分
    5778. 使二进制字符串字符交替的最少反转次数 字符串 滑动窗口
    474. 一和零 dp
  • 原文地址:https://www.cnblogs.com/ET2006/p/Atcoder-2444.html
Copyright © 2020-2023  润新知