AcWing 121 赶牛入圈(二分,前缀和,离散化,尺取)
解题思路
很明显找半径直接二分最小值就行了,但是由于题目给的坐标太大,不可能开一个\(10000^2\)的二维数组求前缀和来写check函数,所以就需要离散化处理。
代码
const int maxn = 5e2+10;
int c,n,fr[maxn],fc[maxn],pre[maxn][maxn],k_fr,k_fc;
P ori[maxn];
bool check(int x) {
for (int s = 1; s<k_fr; ++s) {
//二分找半径之内离枚举的行数最远(指行数差最大)的点
int e = upper_bound(fr+1,fr+k_fr,fr[s]+x-1)-fr;
--e;
int l = 1, r = 1, sum = 0;
//列移动
while(l<k_fc) {
while(r<k_fc && fc[r]-fc[l]+1<=x) {
sum += pre[e][r]-pre[s-1][r];
++r;
}
if (sum>=c) return true;
sum -= pre[e][l]-pre[s-1][l];
++l;
}
}
return false;
}
int main() {
scanf("%d%d",&c,&n);
for (int i = 1; i<=n; ++i) {
scanf("%d%d",&fr[i],&fc[i]);
ori[i] = {fr[i],fc[i]};
}
//行、列离散化
sort(fr+1,fr+n+1);
sort(fc+1,fc+n+1);
k_fr = unique(fr+1,fr+n+1)-fr;
k_fc = unique(fc+1,fc+n+1)-fc;
for (int i = 1; i<=n; ++i) {
int fxx = lower_bound(fr+1,fr+k_fr,ori[i].first)-fr;
int fyy = lower_bound(fc+1,fc+k_fc,ori[i].second)-fc;
++pre[fxx][fyy];
}
//求离散化后的前缀和
for (int i = 1; i<k_fr; ++i)
for (int j = 1; j<k_fc; ++j)
pre[i][j] += pre[i-1][j];
int l = 1, r = 10000;
while(l<r) {
int mid = (l+r)>>1;
if (check(mid)) r = mid;
else l = mid+1;
}
printf("%d\n",l);
return 0;
}