[HNOI2012]三角形覆盖问题

来源:互联网 发布:防止sql注入的原理 编辑:程序博客网 时间:2024/05/21 23:34

题目描述:平面内给定n(<=1w)个直角在左下边平行于坐标轴的等腰直角三角形,求它们的面积并。

输入:n个三角形的左下坐标、直角边长。

输出:面积并大小

(Ps:坐标均为0~100w之间的整数)


求解面积并类问题一般有两种方法:

1.剖分分析 ——圆面积并

2.扫描法 ——矩形面积并


这题初看和矩形面积并很像,对所有关键点设置剖分线后,扫描一遍,用线段树维护即可。

不过麻烦在于,n最多有1w,求交点就需要n^2的时间,并且扫描线上的线段事件也没有矩形那么好处理。


注意到坐标范围只有100w,且都是整数,于是得到另一种扫描法:

数据结构:

用数组ar[]表示当前扫描线,ar[i]记录i位置被多少线段覆盖。

按y排序后的三角形列表。

双向链表保存扫描线上的三角形。

算法过程:

水平扫描线从下往上移动。

利用双向链表里的三角形信息,每次移动时将所有ar[三角形右端位置]--,判断并处理阵亡三角形。

ans += (last_len + now_len)/2 (梯形面积公式)

在对应时刻从排序数组中添加三角形底边。


100w*K的复杂度,K<=n,在随机数据面前稍加优化就成功水过了。代码也非常简单。


Code:

#include<cstdio>#include<algorithm>using namespace std;const int SN=10010, oo=2000000;int n,d,i,x,q,j,h,L[SN],R[SN],tl,w[2000010],tot,la,tmp;struct sjx { int y,l,r; } a[SN];double ans;  bool fini;int getint(){int rn=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();do{rn=rn*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');return rn;}bool Cmp(sjx A, sjx B) { return A.y<B.y; }int main(){     freopen("oj.in", "r", stdin);     freopen("oj.out", "w", stdout);     scanf("%d", &n);     for(i=1; i<=n; i++)      { x=getint(), a[i].y=getint(), d=getint();       a[i].l=x+1, a[i].r=x+d; }     sort(a+1, a+n+1, Cmp);     for(h=0,q=1; h<=oo; h++,la=tot)     {      for(j=R[0]; j; j=R[j])       {           i=a[j].r--;           if(a[j].r<a[j].l)           tl=(j==tl)?L[j]:tl, R[L[j]]=R[j], L[R[j]]=L[j], tmp=L[j],L[j]=R[j]=0,j=tmp;           if((--w[i])==0) tot--;      }      ans+=la+tot;         while(q<=n && a[q].y==h)      {             if(a[q].l>a[q].r) { q++; continue; }             for(j=R[0],fini=false; j&&!fini; j=R[j])               if(a[j].l<=a[q].l && a[j].r>=a[q].r) fini=true;             if(fini) { q++; continue; }           R[tl]=q, L[q]=tl, tl=q;           for(j=a[q].l; j<=a[q].r; j++)           if((w[j]++)==0) tot++;           q++;      }     }     printf("%.1lf\n", ans/2);     return 0;}


原创粉丝点击