金组题什么的都要绕个弯才能AC。。不想银组套模板= =
题目大意:给n个点,求最小边长使得此正方形内的点数不少于c个
首先一看题就知道要二分边长len
本来打算用二维前缀和来判断,显然时间会爆,而且坐标最大10000是不可行的
为保证效率,检验的时间应该在O(n2)
所以我们先给x排个序,以每个点的x坐标为左边界,x+len-1为右边界
然后以y为关键字从小到大序后枚举点,用双指针法O(n)更新len以内能保存多少个点
点数大于等于c就可行
1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 505; 6 int x[maxn],y[maxn],l,r,a[maxn],b[maxn],c,n; 7 8 bool cmp1(int a, int b){return x[a]<x[b];} 9 bool cmp2(int a, int b){return y[a]<y[b];} 10 11 bool check(int len){ 12 int left,right,ans=0; 13 for (int i=1; i<=n; i++){ 14 left=x[a[i]]; right=left+len-1; 15 ans=0; 16 int k=1; 17 for (int j=1; j<=n; j++){ 18 while (y[b[k]]-y[b[j]]+1<=len && k<=n){ 19 if (x[b[k]]>=left && x[b[k]]<=right) { 20 ans++; 21 // printf(" %d %d %d %d %d\n", x[b[j]], x[b[k]], y[b[j]], y[b[k]], ans); 22 } 23 k++; 24 } 25 // printf("%d %d %d %d %d\n", left, right, y[b[j]], y[b[j]]+len-1, ans); 26 if (ans>=c) return 1; 27 if (x[b[j]]>=left && x[b[j]]<=right) ans--; 28 } 29 } 30 return 0; 31 } 32 33 int main(){ 34 scanf("%d%d", &c, &n); 35 for (int i=1; i<=n; i++){ 36 scanf("%d%d", &x[i], &y[i]); 37 r=max(r,x[i]); r=max(r,y[i]); 38 a[i]=b[i]=i; 39 } 40 sort(a+1,a+1+n,cmp1);//x从小到大 枚举列 41 sort(b+1,b+1+n,cmp2);//y从小到大 枚举行 42 l=1; 43 int ans=0; 44 while (l<=r){ 45 int mid=l+r>>1; 46 if (check(mid)) ans=mid,r=mid-1; 47 else l=mid+1; 48 } 49 printf("%d\n", ans); 50 return 0; 51 }