Segment Intersections: Manhattan Geometry
For given nn segments which are parallel to X-axis or Y-axis, find the number of intersections of them.
Input
In the first line, the number of segments nn is given. In the following nn lines, the ii-th segment is given by coordinates of its end points in the following format:
x1y1x2y2x1y1x2y2
The coordinates are given in integers.
Output
Print the number of intersections in a line.
Constraints
- 1≤n≤100,0001≤n≤100,000
- −1,000,000,000≤x1,y1,x2,y2≤1,000,000,000−1,000,000,000≤x1,y1,x2,y2≤1,000,000,000
- Two parallel segments never overlap or touch.
- The number of intersections ≤1,000,000≤1,000,000
Sample Input 1
6 2 2 2 5 1 3 5 3 4 1 4 4 5 2 7 2 6 1 6 3 6 5 6 7
Sample Output 1
3
现在给出n条平行于x轴或者y轴的线段,请输出交点数
可以通过平面扫描(sweep)进行高效求解。平面扫描算法的思路是将一条与x轴(或者y轴)平行的直线向上(向右)平行移动,在移动的过程中寻找交点。这条直线称为扫描线。
扫描线并不是按照固定的间隔进行逐行扫描,而是每次遇到平面上线段的端点时停止移动,然后检查该位置的线段交点。
在扫描移动过程中,算法会将扫描线穿过的垂直线段(与y轴平行)临时记录下来,等到扫描线与水平线段(与x轴平行)重叠时,检查水平线段的范围内是否存在垂直线段上的点,然后将这些点作为交点输出。
平面扫描:
1.将已输入线段的端点按y坐标升序排列,添加至表EP
2.将二叉搜索树T置为空
3.按顺序取出EP的端点(相当于让扫描线自下而上移动),进行以下处理
如果取出的端点为垂直线段的上端点,则从T中删除该线段的x坐标
如果取出的端点为垂直线段的下端点,则将该线段的x坐标插入T
如果取出的端点为水平线段的左端点(扫描线与水平线段重合时),将该水平线段的两个端点作为搜索范围,输出T中包含的值(即垂直线段的x坐标)。
复杂度分析:使用平衡二叉搜索树后,一次搜索操作的复杂度是O(logn),由于这个值小于2n,所以二叉树带来的复杂度为O(nlogn)。算法整个的复杂度还与交点数k有关,所以总的复杂度为O(nlogn+k)。
#define BOTTOM 0
#define LEFT 1
#define RIGHT 2
#define TOP 3
struct point{
double x,y;
point(){}
point(double x,double y):x(x),y(y){}
point operator + (point &p){
return point(x+p.x,y+p.y);
}
point operator - (point &p){
return point(x-p.x,y-p.y);
}
point operator * (double k){
return point(x*k,y*k);
}
point operator / (double k){
return point(x/k,y/k);
}
};
struct segment{
point p1,p2;
};
struct endpoint{
point p;
int seg,st;
endpoint(){}
endpoint(point p,int seg,int st):p(p),seg(seg),st(st){}
bool operator < (const endpoint &ep)const{
if(p.y==ep.p.y){
return st<ep.st;//y相同时,按照下断点,左端点,右端点,上端点的顺序排列
}else{
return p.y<ep.p.y;
}
}
};
endpoint ep[2*maxn];
int manhattanIntersection(vector<segment>s)
{
int n=s.size();
for(int i=0,k=0;i<n;i++){
if(s[i].p1.y==s[i].p2.y){
if(s[i].p1.x>s[i].p2.x){
swap(s[i].p1,s[i].p2);
}
}else if(s[i].p1.y>s[i].p2.y){
swap(s[i].p1,s[i].p2);
}
if(s[i].p1.y==s[i].p2.y){
ep[k++]=endpoint(s[i].p1,i,LEFT);
ep[k++]=endpoint(s[i].p2,i,RIGHT);
}else{
ep[k++]=endpoint(s[i].p1,i,BOTTOM);
ep[k++]=endpoint(s[i].p2,i,TOP);
}
}
sort(ep,ep+(2*n));
set<int>bt;
bt.insert(1000000001);
int cnt=0;
for(int i=0;i<2*n;i++){
if(ep[i].st==TOP){
bt.erase(ep[i].p.x);
}else if(ep[i].st==BOTTOM){
bt.insert(ep[i].p.x);
}else if(ep[i].st==LEFT){
set<int>::iterator b=bt.lower_bound(s[ep[i].seg].p1.x);
set<int>::iterator e=bt.upper_bound(s[ep[i].seg].p2.x);
cnt+=distance(b,e);
}
}
return cnt;
}