标签:线段树
2 10 10 20 20 15 15 25 25.5 0
Test case #1 Total explored area: 180.00
这道题我想了很久,终于A了。。题目是求一些矩形合起来的面积,可以用线段树做。首先用结构体s记录所有矩形的上下边,记录边的左端点x2,右端点x3,纵坐标y,还有增量f(增量的意思是扫到上边的时候总区间减去这条边,扫到下边的时候总区间加上这条边,用1,-1表示),因为线段的坐标都是浮点数,所以要离散化。然后再开一个数组pos,记录所有线段的横坐标,排序后,判断是否有重复值,如果有就跳过,这样做是为了待会建树时方便,因为相同的值建树时没有用。然后建立线段树,用l,r维护每条线段的左右端点,cnt维护这条线段被重复覆盖了几次,0代表没有线段覆盖,大于1表示多次覆盖,len 表示这段区间被覆盖区间的实际总长度,这里对应的是真实坐标的值,并不是线段树中的整数。这道题的线段树比较特别,可以用简单dp做,只有这道题适用,不适用其他题,因为这里保证了有下边就一定有相同长度的上边。
还有最重要的一点(我这里卡了半天= =):就是更新的时候r-1,算总长度的时候r+1;
解释:因为建立线段树的时候我们子区间是这样建的mid=(l+r)/2;build(l,mid),build(mid+1,r),因为对于单纯的线段来说,子区间加起来就是整条线段,但是我们这里不是这样的,因为我们离散化后,mid,mid+1之间也有距离的!所以如果总长度如果由子区间两端加起来其实是pos[r]-pos[mid+1]+pos[mid]-pos[l],少了一段mid~mid+1.所以这里有一个技巧,就是更新的时候r-1,算总长度的时候r+1;
举个例子:要更新的是[1,5],那么我们更新[1,4],然后算的时候算[1,3],[3,4],[4,5],这样就把中间的问题解决了。
动手画一下,就懂了。
#include<iostream> #include<stdio.h> #include<string.h> #include<math.h> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; struct node{ double x2,x3,y; int f; }s[800]; double pos[800]; int m; struct edge{ int l,r,cnt; double len; }b[800]; bool cmp(node a,node b){ return a.y<b.y; } int find(double x) { int mid,l=1,r=m; while(l<=r) { mid=(l+r)/2; if(pos[mid]>x)r=mid-1; else if(pos[mid]<x)l=mid+1; else return mid; } if(pos[l]==x)return l; else return r; } void build(int l,int r,int i) { int mid; b[i].l=l;b[i].r=r;b[i].cnt=b[i].len=0; if(l==r)return; mid=(l+r)/2; build(l,mid,i*2); build(mid+1,r,i*2+1); } void getline(int i) { if(b[i].cnt)b[i].len=pos[b[i].r+1]-pos[b[i].l]; else if(b[i].l==b[i].r)b[i].len=0; else b[i].len=b[i*2].len+b[i*2+1].len; } void update(int l,int r,int add,int i) { int mid; if(b[i].l==l && b[i].r==r){ b[i].cnt+=add; getline(i);return; } mid=(b[i].l+b[i].r)/2; if(r<=mid)update(l,r,add,i*2); else if(l>mid)update(l,r,add,i*2+1); else { update(l,mid,add,i*2); update(mid+1,r,add,i*2+1); } getline(i); } int main() { int n,i,j,num,l,r,k=0; double x2,y2,x3,y3,sum; while(scanf("%d",&n)!=EOF && n!=0) { num=0;sum=0; for(i=1;i<=n;i++){ scanf("%lf%lf%lf%lf",&x2,&y2,&x3,&y3); num++; s[num].x2=x2;s[num].x3=x3;s[num].y=y2;s[num].f=1;pos[num]=x2; num++; s[num].x2=x2;s[num].x3=x3;s[num].y=y3;s[num].f=-1;pos[num]=x3; } sort(s+1,s+1+num,cmp); sort(pos+1,pos+1+num); m=1; for(i=2;i<=num;i++){ if(pos[i]!=pos[i-1]){ m++;pos[m]=pos[i]; } } build(1,m,1); s[0].y=0; for(i=1;i<=num;i++){ sum+=b[1].len*(s[i].y-s[i-1].y); l=find(s[i].x2); r=find(s[i].x3)-1; update(l,r,s[i].f,1); } k++; printf("Test case #%d\n",k); printf("Total explored area: %.2f\n\n",sum); } return 0; }
标签:线段树
原文地址:http://blog.csdn.net/kirito_acmer/article/details/45918499