计算直角坐标系的面积并和面积交和周长并(可小数)
面积并
3个要素:1、离散化,因为坐标可以是浮点数,有些题可能距离很长
2、扫描线,将每个矩形的俩条平行与x轴的俩条边存到数组里,标记为上边和下边,每次扫描到下边的时候,就将这一段统计起来,扫描到下边的时候就将之前的统计去掉;
3、线段树,管理矩形的这些边在x轴方向上的有效距离,实际操作就把这些边一段一段地加到线段树上;
注意:线段树的每一个叶子节点点比如tree[L]维护的是的是区间(x[L],x[L+1]),线段树的其他节点例如(L,R)维护的是区间(x[L],x[R+1]);
cnt是记录当前区间有无被覆盖,要是扫描到下边界的话,这个合法区间的cnt就++,扫描到上边界的话就--;
题:http://acm.hdu.edu.cn/showproblem.php?pid=1542
#include <algorithm> #include <cctype> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iomanip> #include <iostream> #include <map> #include <queue> #include <string> #include <set> #include <vector> using namespace std; #define pb push_back const int M=205; struct node{ double l,r,h; int flag; node(double ll=0,double rr=0,double hh=0,int fl=0):l(ll),r(rr),h(hh),flag(fl){} bool operator <(const node &b)const{ return h<b.h; } }a[M]; struct TREE{ double sum; int cnt; }tree[M<<2]; double lisan[M]; void up(int root,int l,int r){ if(tree[root].cnt) tree[root].sum=lisan[r+1]-lisan[l]; else if(l==r) tree[root].sum=0; else tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum; } void update(int L,int R,int v,int root,int l,int r){ if(L<=l&&r<=R){ tree[root].cnt+=v; up(root,l,r); return ; } int midd=(l+r)>>1; if(L<=midd) update(L,R,v,root<<1,l,midd); if(R>midd) update(L,R,v,root<<1|1,midd+1,r); up(root,l,r);///上传到tree【1】 } int main(){ int t=1,n; while(scanf("%d",&n)==1&&n){ for(int i=0;i<=4*n;i++) tree[i].cnt=0,tree[i].sum=0; for(int i=1;i<=n;i++){ double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); lisan[i]=x1; lisan[i+n]=x2; a[i]=node(x1,x2,y1,1); a[i+n]=node(x1,x2,y2,-1); } sort(lisan+1,lisan+1+2*n); sort(a+1,a+1+2*n); int m=unique(lisan+1,lisan+1+2*n)-lisan-1; double ans=0; for(int i=1;i<2*n;i++){ int l=lower_bound(lisan+1,lisan+1+m,a[i].l)-lisan; int r=lower_bound(lisan+1,lisan+1+m,a[i].r)-lisan; if(l<r)///每个节点控制的是区间(lisan[i],lisan[i+1]) ///区间[lisan[l],lisan[r+1]]对应的原本区间即为(l,r-1); update(l,r-1,a[i].flag,1,1,m); ans+=(a[i+1].h-a[i].h)*tree[1].sum; } printf("Test case #%d\n",t++); printf("Total explored area: %.2f\n\n",ans); } return 0; }
面积交:
分析:实际上这个问题就是问被覆盖俩次及以上的面积是多少,所以我们只要再原基础上再记录一个覆盖俩次的标记长度two,接下来就是分析怎么更新这个two;
要明确一点,就是cnt是实际有意义的,不想lazy值那样是传递的,所以分类讨论一下:
1、当cnt为0时,表示当前区间没有被完全覆盖,那么覆盖一次和覆盖俩次的长度只能由他们的儿子决定;
2、当cnt为1时,表示当前区间被完全覆盖过一次,那么覆盖一次的长度就等于x[R+1]-x[L],而覆盖俩次及以上的部分时完全覆盖一次和这个区间儿子覆盖部分的交集,所以只要算儿子覆盖一次带来的贡献即可;
3,当cnt为2时,表示当前区间被完全覆盖过俩次,覆盖一次和覆盖俩次的长度就为覆盖一次和覆盖俩次的长度;
题:http://acm.hdu.edu.cn/showproblem.php?pid=1255
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int M=2e3+3; struct node{ double l,r,h; int flag; node(double ll=0,double rr=0,double hh=0,int fl=0):l(ll),r(rr),h(hh),flag(fl){} bool operator <(const node &b)const{ return h<b.h; } }a[M]; struct TREE{ double two,one; int cnt; }tree[M<<2]; double lisan[M]; void up(int root,int l,int r){ if(tree[root].cnt>=2) tree[root].two=tree[root].one=lisan[r+1]-lisan[l]; else if(tree[root].cnt==1){ tree[root].one=lisan[r+1]-lisan[l]; if(l==r) tree[root].two=0; else tree[root].two=tree[root<<1].one+tree[root<<1|1].one; } else{ if(l==r) tree[root].one=tree[root].two=0; else{ tree[root].one=tree[root<<1].one+tree[root<<1|1].one; tree[root].two=tree[root<<1].two+tree[root<<1|1].two; } } } void update(int L,int R,int v,int root,int l,int r){ if(L<=l&&r<=R){ tree[root].cnt+=v; up(root,l,r); return ; } int midd=(l+r)>>1; if(L<=midd) update(L,R,v,root<<1,l,midd); if(R>midd) update(L,R,v,root<<1|1,midd+1,r); up(root,l,r);///上传到tree【1】 } int main(){ int t,n; scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;i++){ double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); lisan[i]=x1; lisan[i+n]=x2; a[i]=node(x1,x2,y1,1); a[i+n]=node(x1,x2,y2,-1); } n<<=1; sort(lisan+1,lisan+1+n); sort(a+1,a+1+n); memset(tree,0,sizeof(tree)); int m=unique(lisan+1,lisan+1+n)-lisan-1; double ans=0; for(int i=1;i<n;i++){ int l=lower_bound(lisan+1,lisan+1+m,a[i].l)-lisan; int r=lower_bound(lisan+1,lisan+1+m,a[i].r)-lisan; if(l<r)///每个节点控制的是区间(lisan[i],lisan[i+1]) ///区间[lisan[l],lisan[r+1]]对应的原本区间即为(l,r-1); update(l,r-1,a[i].flag,1,1,m); ans+=(a[i+1].h-a[i].h)*tree[1].two; } printf("%.2f\n",ans); } return 0; }
周长并:
分析:俩条扫面线,沿x轴和沿y轴,对于每次加进来一条边的贡献是加入后的总覆盖区间减去上一次覆盖的区间的绝对值,简单地画一个矩形进行分析
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r const int M=1e4+4; struct node{ int l,r,h; int flag; node(int ll=0,int rr=0,int hh=0,int fl=0):l(ll),r(rr),h(hh),flag(fl){} bool operator <(const node &b)const{ return h<b.h; } }hang[M],shu[M]; struct TREE{ int cnt; int sum; }tr[M<<2]; int all[M],lisanx[M],lisany[M]; void up(int root,int l,int r){ if(tr[root].cnt) tr[root].sum=all[r+1]-all[l]; else if(l==r) tr[root].sum=0; else tr[root].sum=tr[root<<1].sum+tr[root<<1|1].sum; } void update(int L,int R,int v,int root,int l,int r){ if(L<=l&&r<=R){ tr[root].cnt+=v; up(root,l,r); return ; } int midd=(l+r)>>1; if(L<=midd) update(L,R,v,root<<1,l,midd); if(R>midd) update(L,R,v,root<<1|1,midd+1,r); up(root,l,r); } int main(){ int n; while(~scanf("%d",&n)){ for(int i=0;i<=4*n;i++) tr[i].cnt=0,tr[i].sum=0; for(int i=1;i<=n;i++){ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); hang[i]=node(x1,x2,y1,1); hang[i+n]=node(x1,x2,y2,-1); shu[i]=node(y1,y2,x1,1); shu[i+n]=node(y1,y2,x2,-1); lisanx[i]=x1,lisanx[i+n]=x2; lisany[i]=y1,lisany[i+n]=y2; } sort(hang+1,hang+1+2*n); sort(lisanx+1,lisanx+1+2*n); int m=unique(lisanx+1,lisanx+1+2*n)-lisanx-1; for(int i=0;i<=m;i++) all[i]=lisanx[i]; int ans=0; for(int i=1;i<=2*n;i++){ int l=lower_bound(all+1,all+1+m,hang[i].l)-all; int r=lower_bound(all+1,all+1+m,hang[i].r)-all; int las=tr[1].sum; if(l<r) update(l,r-1,hang[i].flag,1,1,m); ans+=abs(tr[1].sum-las); } for(int i=0;i<=4*n;i++) tr[i].cnt=0,tr[i].sum=0; sort(shu+1,shu+1+2*n); sort(lisany+1,lisany+1+2*n); m=unique(lisany+1,lisany+1+2*n)-lisany-1; for(int i=0;i<=m;i++) all[i]=lisany[i]; for(int i=1;i<=2*n;i++){ int l=lower_bound(all+1,all+1+m,shu[i].l)-all; int r=lower_bound(all+1,all+1+m,shu[i].r)-all; int las=tr[1].sum; if(l<r) update(l,r-1,shu[i].flag,1,1,m); ans+=abs(tr[1].sum-las); } printf("%d\n",ans); } return 0; }