题面
以下数据都是整数
有一个平面区域的盒子,左上角x1,y1,右下角x2,y2,现插入n
(
1
<
n
<
=
5000
)
(1<n<=5000)
(1<n<=5000)个隔板(都与盒子上下边界相交,但是互相不相交),并给出划分区域的编号0~n,又给出m(1<m<=5000)个点(不落在盒子外,线上),属于各自的区域。最终输出每个区域有多少个点
多组输入,到n读入0为止
分析
本质上是一个判定点与多边形关系的题。一般的方法有任意划线求与边相交次数,奇内偶外;或是点与各顶点连线后求夹角和(有方向),0外,180边上,360内。
为了判断的更快,不一定要按这两种来。考虑到点都在矩形内,所以可用外积判断点在一个线段的左右:
发现P在UL左边时,
P
L
⃗
×
P
U
⃗
>
0
\vec{PL}×\vec{PU}>0
PL×PU>0,右边相反。
被划分出来的每个区域都是一个四边形,相邻的又能组成一个更大的四边形,即在小四边形内的点一定在大四边形内,这个性质为二分提供了可能:按中位线段划分左右两个四边形,判断点在中位线段左右即可缩小问题规模。
综上,对m个点,每次能在logn次数内给出所属区域,总复杂度O(mlogn)
代码
#include "cstdlib"
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
using namespace std;
struct line
{
int up, down;
}p[5005];
int box[5005];
int n, m, X1, Y1, X2, Y2,xj,yj;//线段数,点数,左上角,右下角,玩具坐标
int check(int pnum)//用外积检验检查xj,yj.在p[pnum]左边,返回true,在右边,返回false
{
if ((p[pnum].down - xj) * (Y1 - yj) - (p[pnum].up - xj) * (Y2 - yj) > 0)return true;
else return false;
}
int find()//二分法找落入哪个盒子
{
int l=0, r=n+1,mid;
while (r-l!=1)
{
mid = (l + r) / 2;
if (check(mid))r = mid;
else l = mid;
}
return l;
}
int main()
{
ios::sync_with_stdio(false);
while (true)
{
cin >> n;
if (n == 0)break;
cin >> m >> X1 >> Y1 >> X2 >> Y2;
p[0].up = X1, p[0].down = X1;
for (int i = 1; i <= n; i++)cin >> p[i].up >> p[i].down;//(up,y1),(down,y2)
p[n+1].up = X2, p[n+1].down = X2;//在p[i]的左边,不在p[i-1]左边,则落入盒子i-1
for (int i = 0; i < m; i++)
{
cin >> xj >> yj;
box[find()]++;
}
for (int i = 0; i <= n; i++)cout << i << ": " << box[i]<<endl;
cout << endl;
memset(box, 0, 5005 * sizeof(int));
}
return 0;
}