题意:
给出平面上一个矩形和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;
}