【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;
}
posted @ 2016-07-19 12:52  Maxwei_wzj  阅读(141)  评论(0编辑  收藏  举报