hdu 5862 Counting Intersections 扫描线+树状数组

题目链接


题意:

给你n条线段,求有多少个交点.


思路:

有感觉是扫描线,可是做的不多不会操作啊 = =.

假设我们用平行于y轴的线段去扫描,那么需要的就是对y坐标进行离散化.

将所有平行于x轴的线段,拆成两个点,(x1,y),(x2,y).,竖线段不变.然后进行离散化,全部按照x坐标升序排序.

然后依次遍历这些线段,遇到线段左端点就把对应y在树状数组中的位置+1,右端点就-1,遇到竖线段就统计他前面的那些 坐标范围在(y1,y2)的有多少个即可.这保证了对于第i条竖向线段,当前树状数组中记录了横坐标横跨该竖向线段的线段数量

这里为了避免横向的右端点x2和竖线段的x相等,我们把所有右端点+1,左端点不变.不过这样需要注意的就是排序时如果遇到坐标相同的,需要把横向的排在竖向的前面.原因就是,我们已经给右交点+1,那么如果原来和竖向相等的,现在+1后二者不可能相等了,+1后还相等说明原来比竖向的小1,不会有交点,我们把他排在竖向的前面,这样扫描的时候减掉就不会有影响了.

#include

using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
int s[maxn];
int cnt ;
int lowbit(int x) 
{
 	
	 return x & -x;
}
void add(int x,int d)
{

 	while(x < maxn)
 	{
 		s[x] += d;
		x += lowbit(x);	
	}
 }
int sum(int x)
{
	int res = 0;
	while(x)
	{
		res += s[x];
		x -= lowbit(x);
	} 
	return res; 
}
struct node
{
	int flag;
	int x,y1,y2;
	node(int _ = 0,int __ = 0,int ___ = 0,int ty = 0)
	{
		x = _;
		y1 = __;
		y2 = ___;
		flag = ty;
	}
	bool operator<(const node &w) const 
	{
		if(x != w.x)
		return x < w.x;
		return flag < w.flag;//排序注意刚好相等时的影响
	} 
}a[maxn];
int c[maxn];
mapmp;
int n;
int main()
{
	int _;
	cin>>_;
	while(_--)
	{
		mp.clear();
		memset(s,0,sizeof s);
		cnt = 1;
		scanf("%d",&n);
		int len = 0;
		int x1,x2,y1,y2;
		int l1 = 0;
		for(int i = 1;i <= n;++i)
		{
			scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
			if(y1==y2)
			{
				if(x1 > x2) swap(x1,x2);
				a[l1++] = node(x1,y1,1,0);
				a[l1++] = node(x2+1,y2,-1,0);
				c[len++] = y1;
			}
			else if(x1 == x2)
			{
				if(y1 > y2) swap(y1,y2);
				a[l1++] = node(x1,y1,y2,1);
				c[len++] = y1;
				c[len++] = y2;
			}
		}
		sort(c,c+len);
		for(int i = 0;i < len;++i)
		{
			if(!mp[c[i]])
			mp[c[i]] = cnt++;
		}
		ll ans = 0;
		sort(a,a+l1);
		for(int i = 0;i < l1;++i)
		{
			if(a[i].flag)
			ans += sum(mp[a[i].y2])-sum(mp[a[i].y1]-1);
			else
			add(mp[a[i].y1],a[i].y2);
		}
		printf("%lld\n",ans);
	}		
	return 0;
} 



你可能感兴趣的:(树状数组,扫描线)