[BZOJ 2731][HNOI 2012]三角形覆盖问题(计算几何+扫描线暴力)
2015-03-10 19:22
330 查看
题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=2731思路
裸的三角形求面积并问题,计算几何问题中这类求面积并的问题的通法就是对事件点做扫描线,不过这个题很有意思,因为它给的所有点的坐标都是整数,而且三角形都是等腰直角三角形,这就使得要讨论的情况简化了许多:1、几乎全部是整数运算,不存在精度问题,不像射箭那题丧病地卡精度,非常爽;2、三角形和三角形的交都是等腰直角三角形,做扫描线时,每次求的面积都是一个直角梯形的面积,并且是顶边一定小于底边的三角形,非常好求。裸做扫描线的做法不难,但是过不了所有数据,但是这种暴力做法还是可以通过优化过掉此题。
首先膜拜下我们湖北第一神犇vfleaking的题解
http://vfleaking.blog.163.com/blog/static/17480763420123301447215/
以下做法就是他的做法,非常简单粗暴。
将所有三角形按照底边的yy坐标升序排序,然后用一根扫描线,从最下面的三角形的底边开始向上扫,并时刻维护sum[]数组和lensum[]数组和len,sum[i]=sum[i]=第ii格上覆盖的三角形个数(注意是第ii格而不是第ii个点!第ii个点和第i+1i+1个点之间的那一格就是第ii格),len=len=扫描线上被覆盖的部分的总长度(以下简称有效长度),显然移动完一次扫描线后,答案中增加的面积=上次扫描线的有效长度+本次扫描线的有效长度2=\frac{上次扫描线的有效长度+本次扫描线的有效长度}{2}
那么实际上整个题要做的就是从最下面不断地向上移动扫描线,用一个栈维护覆盖在扫描线上面的三角形,每移动一次扫描线,先维护一次有效长度和sum[]sum[]数组,但是这次维护只是在原有的三角形基础上减少,并不增加三角形(也就是说这次维护是不添加新相交的三角形的,有效长度只会减少,不会增加)!在答案中添加面积后,再对扫描线上的有效长度和sum[]sum[]数组这两个信息进行第二次维护,这次会加入移动扫描线后新相交的三角形。如此反复便可得到答案。
但是这样做还是会TLE掉最后两个点,因此我们需要再想办法优化,可以发现,某些小三角形是被大三角形所包裹起来的(如下图中红叉的那个紫色三角形,虚线是扫描线,箭头是扫描线的移动方向),显然这样的小三角形都可以忽略不计,因此可以大大减少数据规模。
那么我们可以标记每个三角形是否已经被删除了,在每次扫描线移动后,在加入新的三角形的时候,看这个三角形是否包裹了原来的三角形,以及这个三角形是否被原来的三角形所包裹。若这个三角形包裹了原来的三角形,就把原来的那个三角形删掉,如此下去,如果这个三角形并没有被原来扫描线上的三角形包裹,那么就把它加入扫描线上,并更新对应于扫描线上的区间的格子的信息。
要做到轻松地删除三角形,并通过这样的优化减少数据规模的话,就需要用一个双向链表来维护当前所有还没被删掉的三角形,当然也可以用splay来维护的啦,速度会快很多。
另外这个题的坐标范围比较小,因此扫描线暴力地一格一格移动就行了(就是i++这样),没必要搞离散化,写离散化反而会搞复杂,不过如果坐标范围变大的话,就只能写离散化了。
代码
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 11000 #define MAXL 1100000 using namespace std; double ans=0; //ans=三角形的面积并 int n; //n个三角形 int lowerBound=MAXL,upperBound=-MAXL; //三角形在y坐标上的覆盖区间为[lowerBound,upperBound] int next[MAXN],last[MAXN]; //双向链表,删除三角形时用 int head=1; //链表表头指针 bool availble[MAXN]; //true表示该三角形可以用 int sum[MAXL]; //sum[i]=在当前扫描线上,x坐标为i的点被三角形覆盖的次数 int stack[MAXN],top=0; //stack保存每次移动扫描线后被改的三角形 void del(int p) //在双向链表中删除点p { if(p==head) head=next[p]; next[last[p]]=next[p]; last[next[p]]=last[p]; availble[p]=false; } struct Triangle //三角形 { int x,y,d; //三个点坐标(x,y),(x,y+d),(x+d,y) int l,r; //三角形投影在x轴的区间 }triangles[MAXN]; bool cmp(Triangle a,Triangle b) { if(a.y==b.y) return a.l<=b.l&&a.r>=b.r; return a.y<b.y; } bool operator>(Triangle a,Triangle b) { return a.l<=b.l&&a.r>=b.r; } void init() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d%d",&triangles[i].x,&triangles[i].y,&triangles[i].d); triangles[i].l=triangles[i].x; triangles[i].r=triangles[i].x+triangles[i].d; availble[i]=true; last[i]=i-1; next[i]=i+1; lowerBound=min(lowerBound,triangles[i].y); upperBound=max(upperBound,triangles[i].y+triangles[i].d); } sort(triangles+1,triangles+n+1,cmp); } void work() //求三角形面积并 { int len=0; //len=在当前扫描线上被三角形覆盖的长度 for(int i=1;i<=n&&triangles[i].y==lowerBound;i++) //枚举三角形i for(int j=triangles[i].x;j<triangles[i].r;j++) //枚举扫描线上的坐标j { if(!sum[j]) len++; sum[j]++; } for(int i=lowerBound+1;i<=upperBound;i++) { int lastlen=len; top=0; for(int j=head;triangles[j].y<i&&j<=n;j=next[j]) //在链表中找那些被扫描线穿过的三角形j { Triangle &now=triangles[j]; now.r--; //扫描线向上移了一个单位,对应的,这个三角形的投影区间变成了[L,R-1] if(now.r==now.x-1) //这个三角形扫完了 del(j); //将它从链表中删去 else { if(sum[now.r]==1) len--; //只有一个三角形覆盖了扫描线上now.r的点,这个点不能计入有效长度,那么当前扫描线上的有效长度-1 sum[now.r]--; stack[++top]=j; } } ans=ans+(double)(lastlen+len)/2; //用以lastlen为底边,len为顶边、高为1的梯形更新面积并 //然后更新,把那些被其他大三角形包裹住的小三角形删掉 for(int j=head;triangles[j].y<=i&&j<=n;j=next[j]) //更新扫描线上每个点被三角形覆盖的次数 { int k; if(triangles[j].y==i) { for(k=1;k<=top;k++) { if(availble[stack[k]]==false) continue; if(triangles[stack[k]]>triangles[j]) { del(j); break; } if(triangles[j]>triangles[stack[k]]) { del(stack[k]); //删了栈中的三角形的话,扫描线上每个点被覆盖次数以及扫描线的有效长度必须更新 for(int t=triangles[stack[k]].l;t<triangles[stack[k]].r;t++) { if(sum[t]==1) len--; sum[t]--; } } } if(k>top) //新加入的三角形j没有被原来扫描线上覆盖的三角形所删除,那么它就有可能删除了原来覆盖的三角形,将它对应在扫描线上的区间上的格子都更新一遍 { for(int t=triangles[j].l;t<triangles[j].r;t++) { if(sum[t]==0) len++; sum[t]++; } } } } } } int main() { init(); work(); printf("%.1lf\n",ans); return 0; }
相关文章推荐
- 【bzoj2731】[HNOI2012]三角形覆盖问题
- BZOJ 2731: [HNOI2012]三角形覆盖问题
- BZOJ 2731: [HNOI2012]三角形覆盖问题
- BZOJ 1199 HNOI2005 汤姆的游戏 计算几何+暴力
- 【BZOJ2823】【AHOI2012】信号塔 最小圆覆盖 计算几何
- [HNOI2012]三角形覆盖问题
- [BZOJ1185][HNOI2007]最小矩形覆盖(计算几何-旋转卡壳)
- bzoj1185: [HNOI2007]最小矩形覆盖 计算几何 旋转卡壳
- BZOJ 1199: [HNOI2005]汤姆的游戏 计算几何暴力
- [BZOJ1845][Cqoi2005] 三角形面积并(计算几何+扫描线)
- BZOJ 1845 [Cqoi2005] 三角形面积并 计算几何扫描线
- [HNOI2012]三角形覆盖问题
- [HNOI 2012]三角形覆盖问题
- [BZOJ 2730][HNOI 2012]矿场搭建(Tarjan求割点与桥+计数问题)
- 【计算几何】bzoj2338 [HNOI2011]数矩形
- [BZOJ 1913][APIO 2011]信号覆盖(计算几何)
- BZOJ 1199 HNOI 2005 汤姆的游戏 计算几何
- HYSBZ/BZOJ 1007 [HNOI2008] 水平可见直线 - 计算几何
- [BZOJ1007]HNOI2008水平可见直线|计算几何|栈
- JOJ 2109 && POJ 1981 Circle and Points 计算几何 单位圆覆盖问题