Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 5200   Accepted: 1903

Description

There is a number of disjoint vertical line segments in the plane. We say that two segments are horizontally visible if they can be connected by a horizontal line segment that does not have any common points with other vertical segments. Three different vertical
segments are said to form a triangle of segments if each two of them are horizontally visible. How many triangles can be found in a given set of vertical segments? 





Task 



Write a program which for each data set: 



reads the description of a set of vertical segments, 



computes the number of triangles in this set, 



writes the result. 

Input

The first line of the input contains exactly one positive integer d equal to the number of data sets, 1 <= d <= 20. The data sets follow. 



The first line of each data set contains exactly one integer n, 1 <= n <= 8 000, equal to the number of vertical line segments. 



Each of the following n lines consists of exactly 3 nonnegative integers separated by single spaces: 



yi', yi'', xi - y-coordinate of the beginning of a segment, y-coordinate of its end and its x-coordinate, respectively. The coordinates satisfy 0 <= yi' < yi'' <= 8 000, 0 <= xi <= 8 000. The segments are disjoint.

Output

The output should consist of exactly d lines, one line for each data set. Line i should contain exactly one integer equal to the number of triangles in the i-th data set.

Sample Input

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

Sample Output

1

Source

【题解】

这题的意思是在一个平面里面给你n条线段,是竖直的,然后给你它的横坐标x和竖直方向上的两个端点坐标y1,y2;

然后“可见”的意思是,在这个平面里面放一段水平线段。然后问是否有两条输入的竖直线段被这条水平线段经过。如果有的话还需要这条水平线在这两条线段中间没有穿过其他的输入的竖直线。如果满足上述条件。则称这两条线段可见。只要有任意一条水平线(最少一条)满足就称它们为可见的。

题目说的triangle的意思就是说要找到3条这样两两可见的线段。

问这样的triangle有多少个。

思路:

先按照x轴升序排序那些输入的竖直线段。我这里在排序前记录了它原来的编号。但是我觉得完全可以在排序完之后再记录它们的编号。因为最后只要求数目不要求输出方案;

然后就把这个问题看做是平面上的线段,即从右往左在竖直面上不断地覆盖线段。

覆盖一条线段x的时候,就看一下,当前的竖直面上有哪些之前线段会被覆盖到。如果被覆盖到那说明什么???当然就是它们俩是可见的!

所以我们用一个bo[MAXN][MAXN]来记录某两个编号的线段是否可见。

在做线段树的时候顺便记录就可以了。

然后就是最后的统计数目;

这样

for (int i = 1;i <= n;i++)

for (int j = i+1;j <= n;j++)

if (bo[i][j])

for (int k = i+1;k <= j-1;k++)

if (bo[i][k] && bo[k][j])

ans++;

很容易理解的吧

最后还要提一个问题。

就是类似这样的数据

1 4 0

1 2 1

3 4 1

1 4 2

就是当我们前3个都覆盖了,第4个再覆盖上去编号为4的和编号为1的是否是可见的呢??

显然应该是可见的才对。

因为y坐标的2到3是没有被编号2和编号3线段覆盖的。

但是如果我们正常地按线段树去做会得出1和4是不可见的错解,所以需要把y坐标的对应值都乘上2;

2 8 0

2 4 1

6 8 1

2 8 2

这样线段树就能够判断出[4,6]这个区间是编号1的了。然后4和1就会被判断为可见了。

【代码】

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm> const int MAXN = 8000+10; using namespace std; struct bian //记录线段的信息。
{
int x, y1, y2,bianhao;
}; bian a[MAXN];
int n,color[MAXN*2*4];//color相当于线段树中的lazy_tag现在被用来记录线段的颜色
__int64 ans;//记录答案
bool bo[MAXN][MAXN];//判断任意两条线段是否可见。 void input_data()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d%d", &a[i].y1, &a[i].y2, &a[i].x);
a[i].y1 = a[i].y1*2;a[i].y2 = a[i].y2*2;//为防止错节要乘2
if (a[i].y1 > a[i].y2)
{
int t = a[i].y1;
a[i].y1 = a[i].y2;
a[i].y2 = t;
}
a[i].bianhao = i;
}
} int cmp(const bian &a, const bian &b)
{
if (a.x < b.x)
return 1;
return 0;
} void deal_withlazy(int rt)//处理懒惰标记
{
if (color[rt] != 0)
{
color[rt << 1] = color[rt << 1 | 1] = color[rt];//直接往下传递就可以了。
color[rt] = 0;
}
} void query(int l, int r, int now, int begin, int end, int rt)
{//当前节点的编号为rt,它的区间范围是begin,end;
if (color[rt] != 0)//因为l,r肯定是和begin和end有交集的
{//如果begin和end全部被覆盖成了某种颜色
bo[now][color[rt]] = true;//则这种颜色肯定要被覆盖最少一部分了 则记录它们可见。
bo[color[rt]][now] = true;
return;
}
if (begin >= end)
return;
deal_withlazy(rt);//这句可以省略掉
int m = (begin + end) >> 1;
if (l <= m)
query(l, r, now, begin,m,rt<<1);
if (m < r)
query(l, r, now, m+1,end,rt<<1|1);
} void updata(int l, int r, int now, int begin, int end, int rt)
{
if (l <= begin && end <= r) //如果完全在需要覆盖的里面就直接覆盖颜色
{
color[rt] = now;
return;
}
int m = (begin + end) >> 1;
if (begin >= end)
return;
deal_withlazy(rt);//这句就不能省了,因为要对下面进行操作了。
if (l <= m)
updata(l, r, now, begin,m,rt<<1);
if (m < r)
updata(l, r, now, m+1,end,rt<<1|1);
} void get_ans()
{
for (int i = 1; i <= n; i++)
{
query(a[i].y1, a[i].y2, a[i].bianhao, 0, 16000,1);
updata(a[i].y1, a[i].y2, a[i].bianhao, 0, 16000, 1);
}
for (int i = 1; i <= n - 1; i++)
for (int j = i + 1; j <= n; j++)//求解
if (bo[i][j])
for (int k = i + 1; k <= j - 1; k++)
if (bo[k][i] && bo[k][j])
ans++;
} void init() //每次的初始化。
{
memset(bo, false, sizeof(bo));
memset(color, 0, sizeof(color));
ans = 0;
} void output_ans()
{
cout << ans << endl;
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
//freopen("F:\\rush_out.txt", "w", stdout);
int t;
scanf("%d", &t);
while (t--)
{
init();
input_data();
sort(a + 1, a + 1 + n, cmp);
get_ans();
output_ans();
}
return 0;
}

【37%】【poj1436】Horizontally Visible Segments的更多相关文章

  1. POJ 1436 Horizontally Visible Segments (线段树&#183;区间染色)

    题意   在坐标系中有n条平行于y轴的线段  当一条线段与还有一条线段之间能够连一条平行与x轴的线不与其他线段相交  就视为它们是可见的  问有多少组三条线段两两相互可见 先把全部线段存下来  并按x ...

  2. POJ 1436 Horizontally Visible Segments(线段树)

    POJ 1436 Horizontally Visible Segments 题目链接 线段树处理染色问题,把线段排序.从左往右扫描处理出每一个线段能看到的右边的线段,然后利用bitset维护枚举两个 ...

  3. poj 1436 && zoj 1391 Horizontally Visible Segments (Segment Tree)

    ZOJ :: Problems :: Show Problem 1436 -- Horizontally Visible Segments 用线段树记录表面能被看见的线段的编号,然后覆盖的时候同时把能 ...

  4. (中等) POJ 1436 Horizontally Visible Segments , 线段树+区间更新。

    Description There is a number of disjoint vertical line segments in the plane. We say that two segme ...

  5. 【解题报告】pojP1436 Horizontally Visible Segments

    http://poj.org/problem?id=1436 题目大意:有n条平行于x轴的线段,每条线段有y坐标,如果两条线段有一段x坐标数值相等,且中间没有其它线段阻隔,则称这两条线段"照 ...

  6. poj1436 Horizontally Visible Segments

    这是一个区间更新的题目,先将区间放大两倍,至于为什么要放大可以这样解释,按照从左到右有4个区间,y值是[1,5],[1,2],[3,4],[1,4]如果不放大的话,查询[1,4]区间和前面区间的”可见 ...

  7. POJ 1436 Horizontally Visible Segments

    题意: 有一些平行于y轴的线段 ,两条线段称为互相可见当且仅当存在一条水平线段连接这两条  与其他线段没交点. 最后问有多少组  3条线段,他们两两是可见的. 思路: 线段树,找出两两可见的那些组合, ...

  8. POJ 1436 (线段树 区间染色) Horizontally Visible Segments

    这道题做了快两天了.首先就是按照这些竖直线段的横坐标进行从左到右排序. 将线段的端点投影到y轴上,线段树所维护的信息就是y轴区间内被哪条线段所覆盖. 对于一条线段来说,先查询和它能相连的所有线段,并加 ...

  9. POJ 1436.Horizontally Visible Segments-线段树(区间更新、端点放大2倍)

    水博客,水一水. Horizontally Visible Segments Time Limit: 5000MS   Memory Limit: 65536K Total Submissions:  ...

随机推荐

  1. C#之在treeview中鼠标点击的所选的节点触发事件

    一.背景 如下图所示,我想实现通过鼠标点击treeview的根节点,然后在文本框控件中显示鼠标点击的节点号. 二.程序实现 因为是要通过鼠标点击才发生的事情,所以这属于一个事件,需要触发才行,刚开始不 ...

  2. 在C#中实现Word页眉页脚的全部功能

    页眉页脚经常使用于文章排版,在Word工具栏里.我们能够加入页眉,页脚,页码,日期和时间.图片等信息和内容.页眉/页脚有两个额外选项:首页不同,奇偶页不同.有时在不同的节(section)里插入不同的 ...

  3. log4j的总结

    概述 log4j是日志处理的框架,相当于.net中的log4net.因为之前在.net中学习过log4net.所以.在学习log4j上,感觉很的亲切.本篇博客主要是讲一个图,好了进入正题. log4j ...

  4. 执行spark-shell时遇到的主机地址的错误

    下载了spark 1.4,执行spark-shell时遇到以下错误: java.net.UnknownHostException: UKON-M-Q0EP: UKON-M-Q0EP: nodename ...

  5. ES5, ES6, ES2016, ES.Next: JavaScript 的版本是怎么回事?

    原网址:http://huangxuan.me/2015/09/22/js-version/ JavaScript 有着很奇怪的命名史. 1995 年,它作为网景浏览器(Netscape Naviga ...

  6. 理解OAuth 2.0 - 阮一峰的网络日志

    原文:理解OAuth 2.0 - 阮一峰的网络日志 理解OAuth 2.0 作者: 阮一峰 日期: 2014年5月12日 OAuth是一个关于授权(authorization)的开放网络标准,在全世界 ...

  7. Invalid property 'annotatedClasses' of bean class

    Invalid property 'annotatedClasses' of bean class 在整合Hibernate和Spring时出现,Invalid property 'annotated ...

  8. 一个IP建多个Web站点

      TCP端口法 由于各种原因,我们有时候需要在一个IP地址上建立多个web站点,在IIS5中,我们可能通过简单的设 置达到这个目标. 在IIS中,每个 Web 站点都具有唯一的.由三个部分组成的标识 ...

  9. 关于JS面向对象继承问题

    1.原型继承(是JS中很常用的一种继承方式) 子类children想要继承父类father中的所有的属性和方法(私有+公有),只需要让children.prototype=new father;即可. ...

  10. 2013腾讯编程马拉松||HDU 4505 小Q系列故事——电梯里的爱情 水水水

    http://acm.hdu.edu.cn/showproblem.php?pid=4505 题目大意: 电梯最开始在0层,并且最后必须再回到0层才算一趟任务结束.假设在开始的时候已知电梯内的每个人要 ...