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. 洛谷——P1781 宇宙总统

    https://www.luogu.org/problem/show?pid=1781 题目背景 宇宙总统竞选 题目描述 地球历公元6036年,全宇宙准备竞选一个最贤能的人当总统,共有n个非凡拔尖的人 ...

  2. asp.net Code学习一(vs code跨平台软件操作)

    1.命令行: dotnet new -t web 创建web项目 dotnet new restore build pubilsh run test pack dotnet -info / -h do ...

  3. HTML高级标签之表格标签

    前面学习了一下HTML的经常使用标签, 今天開始高级标签之路! 一.表格标签 1.作用: 创建一张表格 2.各属性作用: <table cellspacing="0" cel ...

  4. 笔记三:JS正则表达式

    正则表达式(英语:Regular Expression,在代码中常简写为regex.regexp或RE)使用单个字符串来描述.匹配一系列符合某个句法规则的字符串搜索模式.说白了正则表达式就是处理字符串 ...

  5. document.write的注意点

    如果给button点击事件添加document.write会消除页面所有元素,包括button按钮 <!DOCTYPE html> <html> <head> &l ...

  6. python,寻找班级里面名字最长的人

    寻找班级里面名字最长的人 我有一串字符串人名:names=(' Kunpen Ji, Li XIAO, Caron Li,' ' Dongjian SHI, Ji ZHAO, Fia YUAN Y,' ...

  7. 基于cropper.js的图片上传和裁剪

    项目中要求图片上传并裁剪的功能,之前也有接触过很多图片裁剪插件,效果体验不是很好,今天推荐一款好用的插件-cropper,超级好用,裁剪功能丰富,满足了各种需求. 功能: 1:点击选择图片,弹出文件夹 ...

  8. 00092_字符输出流Writer

    1.字符输出流Writer (1)既然有专门用于读取字符的流对象,那么肯定也有写的字符流对象: (2)查阅API,发现有一个Writer类,Writer是写入字符流的抽象类.其中描述了相应的写的动作. ...

  9. NYOJ448_寻找最大数【贪心】

    寻找最大数 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描写叙述 请在整数 n 中删除m个数字, 使得余下的数字按原次序组成的新数最大, 比方当n=920813467185 ...

  10. rz、sz (上传下载)命令参数的解释

    $ man rz,查看帮助: -a, –ascii -b, –binary 用binary的方式上传下载,不解释字符为 ascii -e, –escape 强制escape 所有控制字符,比如 Ctr ...