【题目大意】
有一个矩阵的盒子,用隔板分割成很多块,如下图所示:
每个隔板的两端分别在矩阵的上下边界上,且任两个隔板不会相交。现给定矩阵左上角和右下角的坐标,每个隔板两端点的坐标以及一些玩具的坐标,问每个小区域内分布有多少玩具?
输入多个case,每个case第一行输入6个整数:n、m、x1、y1、x2、y2,n是隔板数,m是玩具个数,0<n≦5000,0<m≦5000,( x1 , y1 )、( x2 , y2 )分别是矩阵左上角和右下角坐标。接下来n行每行输入两个数U、L,代表第i个隔板的上下端点坐标分别是( Ui , y1 ),( Li , y2 )。隔板按照从左到右的顺序给出,并且任意两隔板不会相交。接下来m行给出玩具坐标,玩具不会恰好落在隔板上或边界上。单独输入一行0代表输入结束。
输出每个小块中分布的玩具数目。
【输入样例】
5 6 0 10 60 0
3 1
4 3
6 8
10 10
15 30
1 5
2 1
2 8
5 5
40 10
7 9
4 10 0 10 100 0
20 20
40 40
60 60
80 80
5 10
15 10
25 10
35 10
45 10
55 10
65 10
75 10
85 10
95 10
0
【输出样例】
0: 2
1: 1
2: 1
3: 1
4: 0
5: 1
0: 2
1: 2
2: 2
3: 2
4: 2
n个不相交的隔板将矩阵分割成n+1块;由于隔板是按从左到右的顺序给出,所以对于一个确定位置的玩具,可以通过二分计算其所在的块的编号,二分过程只需要找到第一个在其右侧的隔板即可。此处需判断点与线的位置关系,方法见https://blog.youkuaiyun.com/g21glf/article/details/80904435点击打开链接向量叉积一段。
楼楼的代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 5010
struct point{
int x,y;
point(){}
point(int _x,int _y):x(_x),y(_y){}
//定义两种构造函数
point operator-(const point a) const //向量减法
{
return point(x-a.x,y-a.y);
}
int operator*(const point a) const //向量叉乘判断位置
{
return x*a.y-y*a.x;
}
};
int n,m,x1,y1,x2,y2;
int U[N],L[N];
int ans[N]; //ans保存每个小区域有多少玩具,即解
bool ch(int x,int y,int id) //检验编号为id的隔板是否在点(x,y)左侧
{
point a=point(L[id],y2),b=point(U[id],y1);
point c=point(x,y);
return (c-a)*(b-a)>0; //此处直接用叉乘来判断点与直线的位置关系
}
int find(int x,int y) //二分找到点(x,y)所在的区域的编号
{
int l=0,r=n+1,mid;
while(l<r)
{
mid=(l+r)>>1;
if(ch(x,y,mid))
l=mid+1;
else
r=mid;
}
return l-1; //l是点(x,y)右侧第一个隔板,则(x,y)所在的块的编号为l-1
}
int main()
{
while(~scanf("%d",&n),n)
{
int i,j;
memset(ans,0,sizeof(ans)); //注意每次都应初始化ans数组
scanf("%d%d%d%d%d",&m,&x1,&y1,&x2,&y2);
U[0]=L[0]=x1,U[n+1]=L[n+1]=x2;
for(i=1;i<=n;++i)
scanf("%d%d",&U[i],&L[i]);
int x,y;
for(i=0;i<m;++i) //依次输入每个玩具的坐标,并判断其所在区间
{
scanf("%d%d",&x,&y);
ans[find(x,y)]++; //find(x,y)查询点(x,y)所属的区间编号
}
for(i=0;i<=n;++i)
printf("%d: %d\n",i,ans[i]);
cout<<endl;
}
return 0;
}