Corral the Cows赶牛入圈(二维离散化)

题目

原题链接


问题描述

在这里插入图片描述


分析

先考虑直观思路,使用二维前缀和,再二分边长,但是在二维前缀和这一步就会导致时间空间的双爆炸,根据观察,发现数据只有500个,所以可以考虑离散化。
但这里的特别之处是这里是一个二维平面,所以我们的问题是如何对二维平面中的点进行离散化以及如何使用离散化的结果来判断边长是否满足要求

二维离散化

将横坐标依序排好后,记录横坐标值与下标的对应关系,注意去重,因为我们之后是想借助这个相对关系来对点处理,不去重就无法保证相对关系了;
对纵坐标采取相同的操作。

如果一个点之前的坐标是 [ a [ i ] . x , a [ i ] . y ] [a[i].x,a[i].y] [a[i].x,a[i].y],那么将其映射到二维离散化平面的结果也就是其横纵坐标各自离散化的值,这样我们就保证了相对关系没有发生变化。

判断边长是否满足要求

给定边长大小,
如果采用的是一个直接的二维前缀和平面,我们需要枚举出每一个满足要求的点,这样会让时间空间爆炸;
如果是一个离散化的二维平面,我们只需要枚举上面满足要求的点集,同时满足时间空间要求。

策略1:双指针
扫描得到一个满足要求的区间,再扫描纵坐标区间。

策略2:预处理,数据范围不大,一开始可对其都进行预处理,得知每个位置属于的离散化后的标记。
预处理方法链接

代码

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
#define fir(i, a, b) for (ll i = (a); i <= (b); i++)
#define rif(i, a, b) for (ll i = (a); i >= (b); i--)
const int N=5e2+5;
const ll mod=998244353;
ll c,n,s[N][N],sz_x,sz_y;
struct node{
	ll x,y;
};
ll cmp1(node a,node b){ return a.x<b.x;}
ll cmp2(node a,node b){ return a.y<b.y;}
node a[N];
map<ll,ll>mpx,mpy;
vector<ll>vx,vy; 
bool check(ll tmp){//在二维离散化结果中,使用双指针扫描 
	for (int i = 1, k = 0; i <= sz_x; ++ i ) 
		if (vx[i] - vx[1] + 1 >= tmp || i == sz_x){
    	    while (vx[i] - vx[k+1] + 1 > tmp) k ++;//双指针扫描合法区间 
        	for (int j = 1, g = 0; j <= sz_y; ++ j )
				if (vy[j] - vy[1] + 1 >= tmp || j == sz_y) {
        			while (vy[j] - vy[g+1] + 1 > tmp) g ++;
            		if (s[i][j] - s[i][g] - s[k][j] + s[k][g] >= c) return true;
       			}
    	}
    return false;
}
inline void solve(){
	vx.push_back(0);
	vy.push_back(0);
	cin>>c>>n;
	fir(i,1,n){
		cin>>a[i].x>>a[i].y;
		
	}
	//二维离散化(不破坏相对位置)
	sort(a+1,a+n+1,cmp1);
	fir(i,1,n){
		if(!mpx[a[i].x]){
			vx.push_back(a[i].x);
			mpx[a[i].x]=vx.size()-1;
		}
	}
	sort(a+1,a+n+1,cmp2);
	fir(i,1,n){
		if(!mpy[a[i].y]){
			vy.push_back(a[i].y);
			mpy[a[i].y]=vy.size()-1;
		}
	}
	fir(i,1,n){
		s[mpx[a[i].x]][mpy[a[i].y]]++;
	}
	sz_x=vx.size()-1,sz_y=vy.size()-1;
	fir(i,1,sz_x){
		fir(j,1,sz_y){
			s[i][j]+=s[i][j-1]+s[i-1][j]-s[i-1][j-1];
		}
	}
	ll l=1,r=10000,mid;
	while(l<r){
		mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	cout<<r<<endl;
}
int main(){
	ll _;
//	cin>>_;
	_=1;
	while(_--){
		solve();
	}
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值