HDU 5862 Counting Intersections (扫描线+树状数组)

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5862


题意:

2
4
1 0 1 3
2 0 2 3
0 1 3 1
0 2 3 2
4
0 0 2 0
3 0 3 2
3 3 1 3
0 3 0 2


看样例,题意很简单。t个case,每个case第一行n条线段,下面n行每行给出一个线段的端点。这些线段要么与x垂直,要么与y垂直。问能形成多少个交点。不会有重合的线段。


参考博客:http://www.cnblogs.com/fenice/p/5786003.html    感谢!


扫描线这个东西,一直以为是一种算法,现在看来更多的是一种思想吧。

首先将竖直和水平的线段分开来存,把所有的竖直线段按照上端点从大到小排序,再将水平线段按纵坐标y从大到小排序。这样之后我们便可以把水平线段看作一条条直线,从上到下每条直线向下扫,一直扫到不可能再有交点也就是到某个竖直线段的上端点小于水平线段高度,每扫一个点更新树状数组+1。这样扫的同时将每条竖直线段的下端点加入到一个大顶堆里,扫完后再利用优先队列去判断下端点是否满足相交的条件,每不满足一个更新树状数组-1。由于每条线段都要从上往下扫,所以每次开始从上次停止的地方开始扫即可。由于数据量太大,还需要现对x轴上的点进行离散化后再插入到树状数组里。还有几个注意点在注释里标出来了。很巧妙的算法,研究了一天才研究明白。。。太菜了啊。。。大一大二都干嘛去了啊。。。后悔啊。。。区域赛怎么搞啊。。。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
//下标从1开始 ,开4倍最保险,因为一条线段四个点 
const int MAXN = 400100; 
int sum[MAXN];
int N;

struct node {
	int pos, s, e;
	node (int a, int b, int c) : pos(a), s(b), e(c) {}
};
vector<node> x, y;
vector<int> ha;

bool cmpy(node a, node b) {
	return a.e > b.e;
}

bool cmpx(node a, node b) {
	return a.pos > b.pos;
}
int lowbit(int x) {
	return x & (-x);
}
void update(int index, int val) {
	int i;
	for(i = index; i <= 4 * N; i += lowbit(i)) {  //要遍历到最大可能的节点,因为大小不确定 
		sum[i] += val;
	}
}
ll getsum(int index) {
	int i;
	ll res = 0;
	for(i = index; i > 0; i -= lowbit(i)) {
		res += sum[i];
	}
	return res;
}
int main() {
	int t;
	scanf("%d", &t);
	while(t--) {
		memset(sum, 0, sizeof(sum));
		x.clear();
		y.clear();
		ha.clear();
		scanf("%d", &N);
		int x1, y1, x2, y2;
		int i;
		for(i = 0; i < N; i++) {
			scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
			if(y1 == y2) { //横 
				if(x1 > x2) swap(x1, x2);
				x.push_back(node(y1, x1, x2));
				ha.push_back(x1);
				ha.push_back(x2);
			}
			else {
				if(y1 > y2) swap(y1, y2);
				y.push_back(node(x1, y1, y2));
				ha.push_back(x1);
			}
		}
		sort(ha.begin(),ha.end());
		ha.erase(unique(ha.begin(), ha.end()) , ha.end());
		sort(y.begin(), y.end(), cmpy);
		sort(x.begin(), x.end(), cmpx);
		int p = 0;
		ll ans = 0;   //的确有可能超long long 
		priority_queue<pair<int, int> > pq;
		for(i = 0; i < x.size(); i++) {
			while(p < y.size() && x[i].pos <= y[p].e) {
				int id = lower_bound(ha.begin(), ha.end(), y[p].pos) - ha.begin() + 1;
				update(id, 1);
				pq.push(make_pair(y[p].s, id));
				p++;
			}
			while(!pq.empty() && pq.top().first > x[i].pos) {
				update(pq.top().second, -1);
				pq.pop();
			}
			int lid = lower_bound(ha.begin(), ha.end(), x[i].s) - ha.begin() + 1;
			int rid = lower_bound(ha.begin(), ha.end(), x[i].e) - ha.begin() + 1;
			ans += getsum(rid) - getsum(lid - 1);
		}
		printf("%I64d\n", ans);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值