线段树——讲课用——矩形并的周长
题目链接:http://poj.org/problem?id=1177
N<=5000个矩形 坐标范围[-10000,10000] ,均为整数 求矩形并的周长
对横着的和竖着的分别求一次
暴力算法:
对于每一条横边从左到右扫一遍,如果是矩形的下边,先判断当前坐标覆盖数为0,ans+,然后当前坐标覆盖数+1(覆盖数不为0也要加)
如果是矩形的上边,先判断当前坐标覆盖数为1,ans++,然后当前坐标覆盖数-1(覆盖数不为1也要减)
对于每一条竖边,同理(下边改成左边,上边改成右边)
注:1、边[l,r]的扫描范围为[l,r-1]
2、排序时,若几条横边的高度一样,上边优先。同理,竖边左边优先。
原因:这种情况下是一个矩形的下边和另一个矩形的上边重合,两条边的长度都不能算。
如果先扫下边,扫下边之前一定扫了上边,坐标覆盖可能为1,导致答案错误累加
线段树做法:
类似于求矩形并的面积
不同点:累加答案累加与上一次的差,不需要再乘高度
注:1、边[l,r]的扫描范围是[l,r-1]
2、排序时,若几条边高度一样,谁在前无所谓。因为累加与上一次的差
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define delta 10001 #define N 20002 struct rectangle { int x,l,r,f; }tra[N],ver[N]; int sum[N<<2],col[N<<2]; int opl,opr,fl; void read(int &x) { x=0; int f=1; char c=getchar(); while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); } while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } x*=f; } bool cmp1(rectangle a,rectangle b) { return a.x<b.x; } void up(int k,int l,int r) { if(col[k]) sum[k]=r-l+1; else if(l==r) {sum[k]=0; return;} else sum[k]=sum[k<<1]+sum[k<<1|1]; } void change(int k,int l,int r) { if(l>=opl && r<=opr) { col[k]+=fl; up(k,l,r); return; } int mid=l+r>>1; if(opl<=mid) change(k<<1,l,mid); if(opr>mid) change(k<<1|1,mid+1,r); up(k,l,r); } int main() { int n; read(n); int x1,y1,x2,y2; int a,b; for(int i=1;i<=n;++i) { read(x1); read(y1); read(x2); read(y2); x1+=delta; y1+=delta; x2+=delta; y2+=delta; a=i*2-1; b=i*2; tra[a].l=y1; tra[a].r=y2; tra[a].x=x1; tra[a].f=1; tra[b].l=y1; tra[b].r=y2; tra[b].x=x2; tra[b].f=-1; ver[a].l=x1; ver[a].r=x2; ver[a].x=y1; ver[a].f=1; ver[b].l=x1; ver[b].r=x2; ver[b].x=y2; ver[b].f=-1; } int m=n<<1; sort(tra+1,tra+m+1,cmp1); int ans=0,last=0; for(int i=1;i<=m;++i) { opl=tra[i].l; opr=tra[i].r-1; fl=tra[i].f; change(1,1,N-1); ans+=abs(sum[1]-last); last=sum[1]; } sort(ver+1,ver+m+1,cmp1); last=0; for(int i=1;i<=m;++i) { opl=ver[i].l; opr=ver[i].r-1; fl=ver[i].f; change(1,1,N-1); ans+=abs(sum[1]-last); last=sum[1]; } printf("%d",ans); }