【POJ1436】Horizontally Visible Segments-线段树区间更新

(本人本题完成于2016-7-19)
题目大意:给N条垂直于x轴的线段,当两条线段之间可以连一条水平线并且这条水平线不和别的任意垂直线段相交,则称这两条线段是可见的,求满足两两可见的三条线段有多少对。
做法:先将这N条线段以x坐标从小到大排序,再逐一进行下列操作:1.询问该线段的y坐标区间,记录该线段与之前线段之间的可见关系;2.将该线段插入线段树(区间更新)。
注意到线段树是以整数坐标为单位的,这样就会使两条x坐标相等的线段之间有1个单位长度的空隙时,无法判断部分线段之间的可见关系,因此需将所有线段的y坐标*2来处理。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
long d,n,a[8010]={0},b[8010]={0},x[8010]={0},ans;
bool v[8010][8010]={0}; //这里用int会超内存,用bool可以省下大部分内存
struct
{
  long l,r; //区间的左右端点
  long n; //n表示(l,r)区间最后被第n条线段完全覆盖,n为0时表示不能确定
}seg[50010]; //结构体定义线段树的节点

void quicksort(long l,long r)
{
  if (l>=r) return;
  long mid=x[(l+r)/2],i=l,j=r;
  while(i<=j)
  {
    while(x[i]<mid) i++;
	while(x[j]>mid) j--;
	if (i<=j)
	{
	  long temp;
	  temp=a[i];a[i]=a[j];a[j]=temp;
	  temp=b[i];b[i]=b[j];b[j]=temp;
	  temp=x[i];x[i]=x[j];x[j]=temp;
	  i++;j--;
	}
  }
  quicksort(l,j);
  quicksort(i,r);
}

void buildtree(long no,long l,long r)
{
  seg[no].l=l;seg[no].r=r;seg[no].n=0;
  if (l!=r)
  {
    long mid=(l+r)/2;
    buildtree(2*no,l,mid);
	buildtree(2*no+1,mid+1,r);
  }
}

void insert(long no,long s,long t,long x)
{
  if (seg[no].l>=s&&seg[no].r<=t) seg[no].n=x; //区间被完全覆盖,将seg[no].n更新为x
  else
  {
    long mid=(seg[no].l+seg[no].r)/2;
	if (seg[no].n!=0)
	{
	  seg[2*no].n=seg[2*no+1].n=seg[no].n;
	  seg[no].n=0;
	} //将节点信息下放
	if (s<=mid) insert(2*no,s,t,x);
	if (t>mid) insert(2*no+1,s,t,x);
  }
}

void query(long no,long s,long t,long x)
{
  if (seg[no].n!=0) v[seg[no].n][x]=true; //标记第seg[no].n条线段和第x条线段之间的可见关系
  else
  {
    if (seg[no].l!=seg[no].r)
	{
	  if (seg[no].n!=0)
	  {
	    seg[2*no].n=seg[2*no+1].n=seg[no].n;
  	    seg[no].n=0;
	  }
	  long mid=(seg[no].l+seg[no].r)/2;
	  if (s<=mid) query(2*no,s,t,x);
	  if (t>mid) query(2*no+1,s,t,x);
	}
  }
}

int main()
{
  scanf("%ld",&d);
  for(int t=1;t<=d;t++)
  {
    buildtree(1,0,16000);
	scanf("%ld",&n);
	for(int i=1;i<=n;i++)
	{
	  scanf("%ld %ld %ld",&a[i],&b[i],&x[i]);
	  a[i]*=2;b[i]*=2;
	}
	quicksort(1,n);
	memset(v,0,sizeof(v));
	for(int i=1;i<=n;i++)
	{
	  query(1,a[i],b[i],i);
	  insert(1,a[i],b[i],i);
	}
	ans=0;
	for(int i=1;i<=n;i++)
	  for(int j=i+1;j<=n;j++)
	    if (v[i][j])
		{
		  for(int k=i;k<=j;k++)
		    if (v[i][k]&&v[k][j]) ans++;
		} //统计满足条件的线段对
	printf("%ld\n",ans);
  }
  
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值