题意
多个矩形重叠在一起,求出轮廓线的长度。
分析
把矩形分成横线和竖线来处理。现在分析横线的,竖线同理。矩形的坐标都是整数,且范围不大,故此题不需要离散化。从下往上扫描横线,每遇到一条横线,就计算长度,矩形的下边标为1,上边标为-1。具体计算方法是此次区间的总覆盖长度与上次区间长度相比,若没有变化,则说明新加入的横线是重叠的。故每次计算的答案便是两者之差的绝对值。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<algorithm> #include<cstring> #include <queue> #include <vector> #include<bitset> #include<map> #include<deque> using namespace std; typedef long long LL; const int maxn = 10010; const int mod = 77200211+233; typedef pair<int,int> pii; #define X first #define Y second #define pb push_back //#define mp make_pair #define ms(a,b) memset(a,b,sizeof(a)) const int inf = 0x3f3f3f3f; #define lson l,m,2*rt #define rson m+1,r,2*rt+1 struct edge{ int l,r,h,f; }ex[2*maxn],ey[maxn*2]; struct node{ int l,r,cover,len; int mid(){ return (l+r)>>1; } }tree[maxn*8]; int cmp(edge a,edge b){ return a.h<b.h; } void build(int l,int r,int rt){ tree[rt].l=l,tree[rt].r=r; tree[rt].len=tree[rt].cover=0; if(l+1==r) return; int mid = tree[rt].mid(); build(l,mid,rt<<1); build(mid,r,rt<<1|1); } void push_up(int rt){ if(tree[rt].cover){ tree[rt].len=tree[rt].r-tree[rt].l; }else if(tree[rt].l+1==tree[rt].r){ tree[rt].len=0; }else{ tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len; } return; } void update(edge e,int rt){ if(tree[rt].l==e.l&&tree[rt].r==e.r){ tree[rt].cover+=e.f; push_up(rt); return; } int m=tree[rt].mid(); if(e.r<=m) update(e,rt<<1); else if(e.l>=m) update(e,rt<<1|1); else{ edge temp = e; temp.r=m; update(temp,rt<<1); temp = e; temp.l=m; update(temp,rt<<1|1); } push_up(rt); } int ans; void work(int l,int r,edge seg[],int n){ build(l,r,1); int prelen=0; for(int i=0;i<n;i++){ update(seg[i],1); ans += abs(tree[1].len-prelen); prelen=tree[1].len; } } int main(){ int n,x1,x2,y1,y2; while(~scanf("%d",&n)){ if(n==0){ puts("0"); continue; } int maxx,maxy,minx,miny; maxx=maxy=-inf,minx=miny=inf; int cnt=0; for(int i=0;i<n;i++){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); //横向 ex[cnt].l=ex[cnt+1].l=x1, ex[cnt].r=ex[cnt+1].r=x2, ex[cnt].h=y1,ex[cnt+1].h=y2, ex[cnt].f=1,ex[cnt+1].f=-1; ey[cnt].l=ey[cnt+1].l=y1, ey[cnt].r=ey[cnt+1].r=y2, ey[cnt].h=x1,ey[cnt+1].h=x2, ey[cnt].f=1,ey[cnt+1].f=-1; cnt+=2; maxx=max(maxx,max(x1,x2)); minx=min(minx,min(x1,x2)); miny=min(miny,min(y1,y2)); maxy=max(maxy,max(y1,y2)); } sort(ex,ex+cnt,cmp); sort(ey,ey+cnt,cmp); ans=0; work(minx,maxx,ex,cnt); work(miny,maxy,ey,cnt); printf("%d\n",ans); } return 0; }
还有一种更好的做好,参见http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018687.html
同时计算横线和竖线
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 5010 #define MAX 10100 #define lch(i) ((i)<<1) #define rch(i) ((i)<<1|1) struct segment{ int l,r,h,v; }s[2*N]; struct node{ int l,r,lp,rp,cnt,len,num; int mid() { return (l+r)>>1; } }t[2*MAX*4]; int cmp(struct segment p ,struct segment q) { return p.h<q.h; } void build(int l ,int r, int rt) { t[rt].l=l; t[rt].r=r; t[rt].cnt=t[rt].len=0; t[rt].lp=t[rt].rp=t[rt].num=0; if(l==r) return ; int mid=t[rt].mid(); build(l,mid,lch(rt)); build(mid+1,r,rch(rt)); } void cal(int rt) { if(t[rt].cnt) { t[rt].len=t[rt].r-t[rt].l+1; t[rt].lp=t[rt].rp=1; t[rt].num=1; } else if(t[rt].l == t[rt].r) //叶子 { t[rt].len=0; t[rt].lp=t[rt].rp=0; t[rt].num=0; } else { t[rt].len=t[lch(rt)].len+t[rch(rt)].len; t[rt].lp=t[lch(rt)].lp; t[rt].rp=t[rch(rt)].rp; t[rt].num=t[lch(rt)].num + t[rch(rt)].num - (t[lch(rt)].rp&t[rch(rt)].lp); } } void updata(int l ,int r ,int v ,int rt) { if(t[rt].l==l && t[rt].r==r) //目标区间 { t[rt].cnt += v; cal(rt); return ; } int mid=t[rt].mid(); if(r<=mid) updata(l,r,v,lch(rt)); else if(l>mid) updata(l,r,v,rch(rt)); else { updata(l,mid,v,lch(rt)); updata(mid+1,r,v,rch(rt)); } cal(rt); } int main() { int n; while(scanf("%d",&n)!=EOF) { if(n==0) { puts("0"); continue; } int i,maxx,minx,m; for(i=0,m=0,maxx=-MAX,minx=MAX; i<n; i++,m+=2) { int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); maxx=max(maxx,x2); minx=min(minx,x1); s[m].l=x1; s[m].r=x2; s[m].h=y1; s[m].v=1; s[m+1].l=x1; s[m+1].r=x2; s[m+1].h=y2; s[m+1].v=-1; } sort(s,s+m,cmp); build(minx,maxx-1,1); int res=0 , prelen=0; s[m]=s[m+1]; //每次处理循环的最后一次 for(int i=0; i<m; i++) { updata(s[i].l,s[i].r-1,s[i].v,1); res += abs(t[1].len-prelen); //计算横线部分 res += (s[i+1].h-s[i].h)*(2*t[1].num); //计算竖线部分 prelen=t[1].len; } printf("%d\n",res); } return 0; }