两种方法
第一种,对横线和竖线做相同的操作
/* 对横线和竖线做两次一样的操作 这题不需要离散化 */ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 5010 #define MAX 10010 #define lch(i) ((i)<<1) #define rch(i) ((i)<<1|1) int res; struct segment { int l,r,h,v; }sx[2*N],sy[2*N]; struct node{ int l,r,cnt,len; 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; 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; else if(t[rt].l == t[rt].r) //叶子节点 t[rt].len=0; else //由孩子信息所得 t[rt].len=t[lch(rt)].len + t[rch(rt)].len; } 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); } void solve(int left ,int right ,struct segment *s , int n) { build(left,right-1,1); //注意线段树建树,用点建树和用段建树的区别 int ans=0 , prelen=0; for(int i=0; i<n; i++) //updata所有的线段 { updata(s[i].l , s[i].r-1 , s[i].v ,1); ans += abs(t[1].len-prelen); prelen=t[1].len; } res += ans; } int main() { int n; while(scanf("%d",&n)!=EOF) { if(n==0) { puts("0"); continue;} int i,j, maxx=-MAX, minx=MAX, maxy=-MAX, miny=MAX; for(i=0,j=0; i<n; i++,j+=2) { int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); maxx=max(maxx,x2); minx=min(minx,x1); maxy=max(maxy,y2); miny=min(miny,y1); //保存横线的信息 sx[j].l=x1; sx[j].r=x2; sx[j].h=y1; sx[j].v=1; sx[j+1].l=x1; sx[j+1].r=x2; sx[j+1].h=y2; sx[j+1].v=-1; //保存竖线的信息 sy[j].l=y1; sy[j].r=y2; sy[j].h=x1; sy[j].v=1; sy[j+1].l=y1; sy[j+1].r=y2; sy[j+1].h=x2; sy[j+1].v=-1; } sort(sx,sx+j,cmp); //对横线按竖直高度排序 sort(sy,sy+j,cmp); //对竖线按水平高度排序 res=0; solve(minx,maxx,sx,j); //对横线处理一次 solve(miny,maxy,sy,j); //对竖线处理一次 printf("%d\n",res); } return 0; }
第二种:只扫描一次,每次移动都计算两部分值,横线值和竖线值,关键要理解lp,rp,num的作用
#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; }