Segment Intersections: Manhattan Geometry
For given n𝑛 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 𝑛 is given. In the following 𝑛 lines, the 𝑖-th segment is given by coordinates of its end points in the following format:
𝑥1 𝑦1 𝑥2 𝑦2
The coordinates are given in integers.
Output
Print the number of intersections in a line.
Constraints
- 1 ≤𝑛 ≤ 100,000
- −1,000,000,000 ≤ 𝑥1, 𝑦1, 𝑥2, 𝑦2 ≤ 1,000,000,000
- Two parallel segments never overlap or touch.
- The number of intersections ≤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
题意:
给定若干垂直于坐标轴(有些垂直于X轴,有些垂直于Y轴)的线段,查询整个区间中有多少个交点。
题解:
采用扫描线的思想,想象一根水平直线从下向上扫描,对于每一个存在水平线段的区间进行查询(也就是线段树里面的query操作),看该区间内有多少交点。
那么如何记录有没有交点呢?
这里注意,我们的思路是从下向上,一层一层的扫描,针对每个拥有水平直线的区间查询是否有竖直直线,如果有,我们记为1。
而一条线段是有长度的,也就是说,对于一个区间内,不可能总存在有一条竖直选段,这就需要用到我们的线段树内的 modify 操作了:
当从下向上扫描的过程中,遇到了一条竖直直线的下端点,就在该点(单点修改)+1;
而遇到直线上端点的时候(单点修改)-1
这样我们每换一条线就对线段树做n次修改,再进行m次查询,记录总和即可
由于数据量很大,不可能对于每一个横坐标都单独开一个空间存储,所以本题还要用到离散化。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
const int N = 1e6 + 10;
struct Point
{
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y) { }
};
struct segment
{
Point A, B;
int c;
segment(Point A, Point B, int c) :A(A), B(B), c(c) { }
bool operator< (const segment& p)
{
if (A.y != p.A.y) return A.y < p.A.y;
return c != p.c ? c < p.c : A.x < p.A.x;
}
};
vector<segment> segs;
vector<int> xs;
int x[N * 2];
struct Node
{
int l, r;
int cnt;
}tr[N << 2];
void pushup(Node& u, Node& l, Node& r)
{
u.cnt = l.cnt + r.cnt;
}
void pushup(int u)
{
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r)
{
tr[u].l = l;
tr[u].r = r;
if (l == r)
{
tr[u].cnt = 0;
return ;
}
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
void modify(int u, int x, int k)
{
if (tr[u].l == x && tr[u].r == x)
{
tr[u].cnt += k;
//pushup(u);
}
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (mid >= x) modify(u << 1, x, k);
if (mid < x) modify(u << 1 | 1, x, k);
pushup(u);
}
}
int query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].cnt;
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if (l <= mid) sum = query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
int find(int x)
{
return lower_bound(xs.begin(), xs.end(), x) - xs.begin();
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
Point A, B;
cin >> A.x >> A.y >> B.x >> B.y;
if (A.x == B.x) // 如果是竖线
{
if (A.y > B.y) swap(A, B);
xs.push_back(A.x);
segs.push_back(segment(A, A, 1));
segs.push_back(segment(B, B, 3));
}
else
{
if (A.x > B.x) swap(A, B);
xs.push_back(A.x);
xs.push_back(B.x);
segs.push_back(segment(A, B, 2));
}
}
sort(segs.begin(), segs.end());
sort(xs.begin(), xs.end());
xs.erase(unique(xs.begin(), xs.end()), xs.end());
build(1, 0, xs.size() - 1);
int cnt = segs.size();
int ans = 0;
for (int i = 0; i < cnt; i++)
{
if (segs[i].A.x == segs[i].B.x) //线段竖直
{
int id = find(segs[i].A.x);
int c = segs[i].c == 3 ? -1 : 1;
modify(1, id, c);
}
else
{
int l = find(segs[i].A.x), r = find(segs[i].B.x);
ans += query(1, l, r);
}
}
cout << ans << endl;
return 0;
}