[AIZU ONLINE JUDGE] 计算几何 CGL_6_A(扫描线+线段树)

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;
}

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值