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. 【2017 Multi-University Training Contest - Team 9】Numbers

    [链接]http://acm.hdu.edu.cn/showproblem.php?pid=6168 [题意] 有一个长度为n的序列a1--an,根据a序列生成了一个b序列,b[i] = a[i]+a ...

  2. 《ASP.NET》数据绑定—DropDownList、ListBox

    DropDownList和ListBox实现两级联动功能.他们也能够将从后台数据库中搜选的出来的信息加以绑定.这里要实现的功能是在DropDownList中选择"省",然后让Lis ...

  3. 看好腾讯,鄙视百度(腾讯的核心竞争力,不是超过10亿的QQ的注册用户,也不是某一项产品、技术方面优势,而是“耐心”:懂得在合适的时间推出合适的产品。”)

    百度,自始至终只是一个低劣的模仿者,且一切向前看,完全违背了一个搜索引擎所应该遵循的基本原则.谁给的钱多就能搜着谁,这跟贩毒有什么区别? 腾讯也在模仿别人,但是,它是模仿然后超越.在中国互联网发展历史 ...

  4. Android时间对话框TimePickerDialog介绍

    目前网上流行着很多对“时间对话框TimePickerDialog”的讲解文章,但感觉都不是很详细.这里详细对该方面的知识进行介绍,旨在帮助初学者能够快速掌握该项技术. 首先要做的是声明一个日历类的对象 ...

  5. amazeui学习笔记二(进阶开发4)--JavaScript规范Rules

    amazeui学习笔记二(进阶开发4)--JavaScript规范Rules 一.总结 1.注释规范总原则: As short as possible(如无必要,勿增注释):尽量提高代码本身的清晰性. ...

  6. JS原生选项卡 – 幻灯片效果

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title> ...

  7. amazeui学习笔记--css(常用组件6)--图标Icon

    amazeui学习笔记--css(常用组件6)--图标Icon 一.总结 1.关注用法即可:在 HTML 上添加添加 am-icon-{图标名称} class. <span class=&quo ...

  8. jQuery中$(document).ready()和window.onload的区别?

    document.ready和document.load的区别?(JQ中的$(document).ready()和window.onload的区别?) window.onload,是采用DOM0级事件 ...

  9. HTML(超文本标记语言)的内容和理解

    由于上篇文章中提到WebMethod的Description 属性(propery)中可以使用超文本,因此就记录一篇关于超文本的文章以供参考,注意:Description=" HTML格式  ...

  10. echarts3.0 仪表盘实例更改完成占用率实例

    需要完成的项目效果 官方实例效果 基本思路: 首先引入jquery和echarts3.0库. 需要两个仪表盘,一个仪表盘是纯色灰色,在底部.startAngle 和endAngle永远是最大值,默认为 ...