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<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; //这里是叶子节点,已经不是一条线段,所以是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; }代码二:以段建树,用了一个简单动规,只适合本题
#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,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; }
代码三:适用范围广,可以用于其他增减线段的题
#include<iostream>#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<algorithm> using namespace std; #define maxn 250 struct edge{ double x2,x3,y; int f; }a[maxn]; double num,pos[maxn]; int n,m; bool cmp(edge a,edge b){ return a.y<b.y; } struct node{ int l,r,cnt; double len; }b[4*maxn]; 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 update(int l,int r,int add,int i) { int mid; if(b[i].cnt!=-1 && b[i].l==l && b[i].r==r){ b[i].cnt+=add;return; } if(b[i].cnt!=-1){ b[i*2].cnt=b[i*2+1].cnt=b[i].cnt; b[i].cnt=-1; } 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); } } void question(int l,int r,int i) { int mid; if(b[i].l==l && b[i].r==r && b[i].cnt>0){ num+=pos[b[i].r+1]-pos[b[i].l];return; } if(b[i].cnt==0)return; mid=(b[i].l+b[i].r)/2; if(r<=mid)question(l,r,i*2); else if(l>mid)question(l,r,i*2+1); else { question(l,mid,i*2); question(mid+1,r,i*2+1); } } int find(int l,int r,double x) { int mid; while(l<=r){ mid=(l+r)/2; if(pos[mid]==x)return mid; else if(pos[mid]>x)r=mid-1; else l=mid+1; } if(pos[l]==x)return l; else return r; } int main() { int i,j,t,t1,t2,num1=0; double c,d,e,f,x,y,xx,yy,sum; while(scanf("%d",&n)!=EOF && n!=0) { t=0; for(i=1;i<=n;i++){ scanf("%lf%lf%lf%lf",&x,&y,&xx,&yy); pos[i]=x;pos[i+n]=xx; t++; a[t].y=y;a[t].x2=x;a[t].x3=xx;a[t].f=1; t++; a[t].y=yy;a[t].x2=x;a[t].x3=xx;a[t].f=-1; } sort(pos+1,pos+1+2*n); m=1; for(i=2;i<=2*n;i++){ if(pos[m]!=pos[i]){ m++;pos[m]=pos[i]; } } build(1,m,1); sum=0; sort(a+1,a+1+2*n,cmp); a[0].y=0; for(i=1;i<=2*n;i++){ num=0; question(1,m-1,1); sum+=num*(a[i].y-a[i-1].y); t1=find(1,m,a[i].x2); t2=find(1,m,a[i].x3); update(t1,t2-1,a[i].f,1); } num1++; printf("Test case #%d\n",num1); printf("Total explored area: %.2f\n",sum); printf("\n"); } return 0; }