题意:
给出平面上一个矩形和n个纵向分割矩形的直线;
直线之间互不相交,将矩形分割为n-1个块;
现在矩形上有m个点,求每个块内有几个点;
n,m<=5000,数据保证点严格在块的内部(不在直线上);
题解:
首先将直线求出来,然后最朴素的想法是拿一个点一一和块去比较;
这样复杂度是O(nm)的,并且实际上做了许多无用的计算;
例如:当你用一个点去比较一个块之后,实际上已经可以知道这个点是在块的左面还是右面了;
也就是说每次比较就可以去掉一半的块,即二分求解;
由于左右对称,所以我们只需要判断点是否在块右边界的左面;
这个满足二分性质,用叉积来判断之后,可以O(logn)求解;
总体复杂度O(mlogn);
代码:
#include<math.h> #include<stdio.h> #include<string.h> #include<algorithm> #define N 5100 using namespace std; const double EPS=1e-6; const double INF=1e100; struct Point { double x,y; friend Point operator -(Point a,Point b) { Point temp; temp.x=a.x-b.x,temp.y=a.y-b.y; return temp; } friend Point operator +(Point a,Point b) { Point temp; temp.x=a.x+b.x,temp.y=a.y+b.y; return temp; } friend Point operator *(double a,Point b) { Point temp; temp.x=a*b.x,temp.y=a*b.y; return temp; } friend double operator ^(Point a,Point b) { return a.x*b.y-a.y*b.x; } }st,en; struct Line { Point p,v; int cnt; void Build(double a,double b) { p=st; Point u=en; p.x=a; u.x=b; v=u-p; } bool check(Point t) { if((v^(t-p))>0) return 1; else return 0; } }li[N]; int main() { int n,m,i,j,k,l,r,mid; double a,b; Point t; while(scanf("%d",&n)&&n) { memset(li,0,sizeof(li)); scanf("%d",&m); scanf("%lf%lf%lf%lf",&st.x,&en.y,&en.x,&st.y); li[0].Build(st.x,st.x); li[n+1].Build(en.x,en.x); for(i=1;i<=n;i++) { scanf("%lf%lf",&a,&b); li[i].Build(b,a); } for(i=1;i<=m;i++) { scanf("%lf%lf",&t.x,&t.y); l=0,r=n+1; while(l<=r) { mid=l+r>>1; if(li[mid].check(t)) r=mid-1; else l=mid+1; } li[l].cnt++; } for(i=1;i<=n+1;i++) { printf("%d: %d\n",i-1,li[i].cnt); } putchar('\n'); } return 0; }
原文地址:http://blog.csdn.net/ww140142/article/details/47167493