[HNOI 2012]三角形覆盖问题

Description

二维平面中,给定   N个等腰直角三角形(每个三角形的两条直角边分别

平行于坐标轴,斜边从左上到右下)。我们用三个非负整数( xyd)来描

述这样一个三角形,三角形三个顶点的坐标

分别为(xy), (x + dy)和(xy +     d)。要求计算这   N个三角形所覆盖的总面

积。例如,下图有 3 个三角形,覆盖的总面积为 11.0。

Input

输入文件第一行为一个正整数N,表示三角形的个数。接下来的 N

行每行有用空格隔开的三个非负整数,  xy   , d,描述一个三角

形的顶点坐标,分别为

(    xy), (x + dy), (  xy+d),

其中 xyd 满足0≤   xyd≤1000000。

对于50%的数据,1≤         N≤500;

100%的数据,1≤N≤10000。

Output

仅包含一行,为一个实数    S    ,表示所有三角形所覆盖的总面积,输出恰

好保留一位小数。输入数据保证     S≤2^31 。

Sample Input

3 .
1 1 4
2 0 2
3 2 2

Sample Output

11.0

题解:

将所有三角形按照底边的y坐标升序排序,然后用一根扫描线,从最下面的三角形的底边开始向上扫,并时刻维护sum[]数组和len,sum[i]=第i格上覆盖的三角形个数(注意是第i格而不是第i个点!第i个点和第i+1个点之间的那一格就是第i格),len=扫描线上被覆盖的部分的总长度(以下简称有效长度),显然移动完一次扫描线后,答案中增加的面积=$$\frac{上次扫描线的有效长度+本次扫描线的有效长度}{2}$$

那么实际上整个题要做的就是从最下面不断地向上移动扫描线,用一个栈维护覆盖在扫描线上面的三角形,每移动一次扫描线,先维护一次有效长度和sum[]数组,但是这次维护只是在原有的三角形基础上减少,并不增加三角形(也就是说这次维护是不添加新相交的三角形的,有效长度只会减少,不会增加)!在答案中添加面积后,再对扫描线上的有效长度和sum[]数组这两个信息进行第二次维护,这次会加入移动扫描线后新相交的三角形。如此反复便可得到答案。

但是这样做还是会TLE掉最后两个点,因此我们需要再想办法优化,可以发现,某些小三角形是被大三角形所包裹起来的(如下图中红叉的那个紫色三角形,虚线是扫描线,箭头是扫描线的移动方向),显然这样的小三角形都可以忽略不计,因此可以大大减少数据规模。

那么我们可以标记每个三角形是否已经被删除了,在每次扫描线移动后,在加入新的三角形的时候,看这个三角形是否包裹了原来的三角形,以及这个三角形是否被原来的三角形所包裹。若这个三角形包裹了原来的三角形,就把原来的那个三角形删掉,如此下去,如果这个三角形并没有被原来扫描线上的三角形包裹,那么就把它加入扫描线上,并更新对应于扫描线上的区间的格子的信息。 
要做到轻松地删除三角形,并通过这样的优化减少数据规模的话,就需要用一个双向链表来维护当前所有还没被删掉的三角形,当然也可以用splay来维护的啦,速度会快很多。

[原文链接]

 1 //Never forget why you start
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<cmath>
 7 #include<algorithm>
 8 using namespace std;
 9 int n,m;
10 struct triangle {
11     int x,y,d,l,r;//横坐标,纵坐标,直角边长,左端点横坐标,右端点横坐标
12     friend bool operator > (const triangle a,const triangle b) {
13         return a.l<=b.l&&b.r<=a.r;
14     }//定义一个三角形a左右两端点如果完全包含另外一个三角形b,则a>b
15 } a[10005];
16 int Next[10005],pre[10005],vis[10005],head,mmax,mmin,len,lastlen,sum[2000005],top,stack[10005];
17 //Next[]表示双向链表的下一个数
18 //pre[]表示双向链表的上一个数
19 //vis[]表示是否在双向链表中
20 //head表示双向链表的第一个点
21 double ans;
22 void delet(int x) {
23     vis[x]=0;
24     if(head==x)head=Next[x];
25     pre[Next[x]]=pre[x];
26     Next[pre[x]]=Next[x];
27 }//从双向链表中删除一个数
28 bool cmp(triangle a,triangle b) {
29     if(a.y==b.y)return a.l<=b.l&&b.r<=a.r;
30     else return a.y<b.y;
31 }//比较函数,先按y从小到大排序,再使前面的三角形完全包含后面的三角形
32 int main() {
33     int i,j,k,l;
34     scanf("%d",&n);
35     for(i=1; i<=n; i++) {
36         scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].d);
37         a[i].r=a[i].x+a[i].d;
38         a[i].l=a[i].x;
39         vis[i]=1;
40         Next[i]=i+1;
41         pre[i]=i-1;
42         mmin=min(mmin,a[i].y);
43         mmax=max(mmax,a[i].y+a[i].d);
44     }//输入,初始化双向链表
45     sort(a+1,a+n+1,cmp);//排序
46     head=1;
47     for(i=head; i<=n&&a[i].y==mmin; i=Next[i]) {
48         for(j=a[i].l; j<a[i].r; j++) {
49             if(!sum[j])len++;
50             sum[j]++;
51         }
52     }//统计最开始被三角形覆盖的长度
53     for(i=mmin+1; i<=mmax; i++) {
54         lastlen=len;//lastlen表示层被覆盖的长度,len表示这一层备覆盖的长度
55         top=0;//初始化栈
56         for(j=head; j<=n&&a[j].y<i; j=Next[j]) {//遍历上一层已经计算过的三角形
57             a[j].r--;//因为向上挪了一层,就相当于宽度减小了1
58             if(a[j].r<a[j].l)delet(j);//如果这个三角形完全消失,则从双向链表中删去
59             else {
60                 if(sum[a[j].r]==1)len--;//判断r--后是否对len的大小造成影响
61                 sum[a[j].r]--;
62                 stack[++top]=j;//将这个三角形压入栈中(这个三角形覆盖了一部分长度)
63             }
64         }
65         ans+=(double)(lastlen+len)/2.0;//计算答案
66         for(j=head; j<=n&&a[j].y<=i; j=Next[j]) {
67             if(a[j].y==i) {//遍历所有覆盖在当前层的三角形
68                 for(k=1; k<=top; k++) {//遍历所有在栈中的三角形,
69                     if(vis[stack[k]]==0)continue;
70                     if(a[stack[k]]>a[j]) {//判断当前三角形是非已被栈中某个三角形覆盖
71                         delet(j);//如果已经覆盖,就在双向链表中将这个三角形删除
72                         break;
73                     }
74                     if(a[j]>a[stack[k]]) {
75                         delet(stack[k]);
76                         for(l=a[stack[k]].l; l<a[stack[k]].r; l++) {
77                             if(sum[l]==1)len--;
78                             sum[l]--;
79                         }//如果栈中三角形被当前三角形覆盖,就删除栈中三角形
80                     }
81                 }
82                 if(k==top+1) {//如果所有栈中三角形都无法将当前三角形覆盖,就加入当前三角形
83                     for(l=a[j].l; l<a[j].r; l++) {
84                         if(!sum[l])len++;
85                         sum[l]++;
86                     }
87                 }
88             }
89         }
90     }
91     printf("%.1lf\n",ans);
92     return 0;
93 }

原文地址:https://www.cnblogs.com/huangdalaofighting/p/8260082.html

时间: 2024-11-08 20:54:45

[HNOI 2012]三角形覆盖问题的相关文章

HNOI 2012 永无乡

codevs 1477 永无乡 http://codevs.cn/problem/1477/ 2012年湖南湖北省队选拔赛 时间限制: 1 s 空间限制: 128000 KB 题目描述 Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连通的.现

HNOI 2012/codevs 1994:排队

题目描述 Description 某中学有n 名男同学,m 名女同学和两名老师要排队参加体检.他们排成一条直线,并且任意两名女同学不能相邻,两名老师也不能相邻,那么一共有多少种排法呢?(注意:任意两个人都是不同的) 输入描述 Input Description 输入文件只有一行且为用空格隔开的两个非负整数n 和m,其含义如上所述. 输出描述 Output Description 仅包含一个非负整数,表示不同的排法个数.注意答案可能很大. 样例输入 Sample Input 样例输入1 1 1 样

[BZOJ 2730][HNOI 2012] 矿场搭建

2730: [HNOI2012]矿场搭建 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2113  Solved: 979[Submit][Status][Discuss] Description 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口.请写一个程序,用

BZOJ 2732 HNOI 2012 射箭 半平面交

题目大意:给出一些与x轴垂直的线段,问一个经过原点的抛物线最多能按顺序经过多少条线段. 思路:总体上来说是数学题,我们来推一推. 设这个经过原点的抛物线为y = a * x ^ 2 + b * x,设一条线段的起点和终点为(x0,y1)和(x0,y2),且y2 > y1. 将x0带入到设出的抛物线中,会得到y = a * x0 ^ 2 + b * x0,这时候需要满足的是y <= y2 && y >= y1,也就是a * x0 ^ 2 + b * x0 <= y2

BZOJ 2733 HNOI 2012 永无乡 平衡树启发式合并

题目大意:有一些岛屿,一开始由一些无向边连接.后来也有不断的无向边加入,每一个岛屿有个一独一无二的重要度,问任意时刻的与一个岛屿联通的所有岛中重要度第k大的岛的编号是什么. 思路:首先连通性一定要用并查集维护,然后就是联通快内的第k大问题,显然是平衡树.但是并查集的合并怎么搞?可以考虑按秩合并,这样的话就保证每次在平衡树中处理的元素尽量的少,就可以水过这个题了. 注意一下输出-1的判断. CODE: #include <map> #include <cstdio> #include

css实现的透明三角形

css实现下图样式,具体像素值记不住了,很好设置,html code (2014百度秋招面试题): <div id="demo"></div>   分析:这个样式的关键就在三角形和三角形实现了之后的变成只有个边框的三角形.利用元素的:after和:before伪元素(请自动忽略低版本IE). 思想:先实现个正方形,在实现个三角形层,放在右上角,然后再实现一个透明的三角形覆盖黑色三角形的内部,只留边框. 1 <!DOCTYPE html> 2 <

A Quick Overview of MSAA

A Quick Overview of MSAA 原文地址:https://mynameismjp.wordpress.com/2012/10/24/msaa-overview/ Previous article in the series: Applying Sampling Theory to Real-Time Graphics MSAA can be a bit complicated, due to the fact that it affects nearly the entire

实现微信聊天的尖角图片样式

参考连接 http://www.jianshu.com/p/f77f54720202,不过我感觉他这里实现的不是很完美,因为三角是继承的整个图片,所以做了一些更改 效果如下,哈哈在safari里一放大明显能看出来怎么弄的 第一种实现方式 如上图,方式是一个透明三角和两个矩形去覆盖图片右侧内容,不过这样会是原本图片右侧的两个border-radius失效,然后再弄两个圆弧盖住右上角和右下角..感觉有点傻,在android上还有兼容性问题,代码如下,效果如下图 1 <!doctype html>

Summer training round2 #5

A:正着DFS一次处理出每个节点有多少个优先级比他低的(包括自己)作为值v[i] 求A B 再反着DFS求优先级比自己高的求C #include <bits/stdc++.h> #include <cstring> #include <iostream> #include <algorithm> #define EPS 1.0e-9 #define PI acos(-1.0) #define INF 30000000 #define MOD 10000000