线段树——扫描线
PKU 1151 && hdu1542 Atlantis 矩形面积并
http://poj.org/problem?id=1151
题意:
给出n个矩形,每个矩形给出左下角坐标,右上角坐标。然后求矩形并的总面积;
思路:
浮点数先要离散化;然后把矩形分成两条边,上边和下边,对横轴建树,然后从下到上扫描上去,用cnt表示该区间下边比上边多几个,sum代表该区间内被覆盖的线段的长度总和
这里线段树的一个结点并非是线段的一个端点,而是该端点和下一个端点间的线段,所以题目中r+1,r-1的地方可以自己好好的琢磨一下;
这里点到线段的转化不好理解:
我们的线段树的叶子节点表示的是线段,座椅如果我们求1到4的线段长度是实际上是求的1,2,3好线段,所以r要-1,而当我们真正求长度是是X[i + 1] - X[i]才是i线段的长度。
View Code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 227 using namespace std; //freopen("din.txt","r",stdin); struct Seg{ double l,r,h; int mk; Seg(){} Seg(double L,double R,double H,int MK){ l = L; r = R; h = H; mk = MK; } bool operator < (const Seg &tmp) const{ return h < tmp.h; } }seg[N]; int cnt[N<<2]; double sum[N<<2],X[N]; void pushup(int rt,int l,int r){ if (cnt[rt]) sum[rt] = X[r + 1] - X[l];//下边比上边多此区间仍满足条件 else if (l == r) sum[rt] = 0;//单个的一条线段且x长度不满足条件肯定为0 else sum[rt] = sum[rt<<1] + sum[rt<<1|1];//x不满足条件但其子树可能存在满足条件的 } void update(int L,int R,int sc,int l,int r,int rt){ if (L <= l && r <= R){ cnt[rt] += sc;//更新 pushup(rt,l,r); return ; } int m = (l + r)>>1; if (L <= m) update(L,R,sc,lc); if (m < R) update(L,R,sc,rc); pushup(rt,l,r); } //离散化后二分查找 int bSearch(double val,int n){ int l = 0; int r = n; while (l <= r){ int m = (l + r)>>1; if (val == X[m]) return m; else if (val < X[m]) r = m - 1; else l = m + 1; } return l; } int main(){ //freopen("din.txt","r",stdin); int i; int n; int cas = 1; double x1,y1,x2,y2; while (~scanf("%d",&n)){ if (!n) break; int len = 0; //按x轴建树,记录横向线段以及x左边 for (i = 0; i < n; ++i){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); X[len] = x1; seg[len++] = Seg(x1,x2,y1,1); X[len] = x2; seg[len++] = Seg(x1,x2,y2,-1); } //离散化 sort(X,X + len); sort(seg,seg + len); int k = 1; for (i = 1; i < len; ++i){ if (X[i] != X[i - 1]) X[k++] = X[i]; } //线段树扫描处理 double res = 0; CL(sum, 0); CL(cnt,0); for (i = 0; i < len - 1; ++i){ int l = bSearch(seg[i].l,k - 1); int r = bSearch(seg[i].r,k - 1) - 1;//这里讲点转化成线段(难理解) if (l <= r) update(l,r,seg[i].mk,0,k - 2,1); res += sum[1]*(seg[i + 1].h - seg[i].h);//sum[1]记录整个区间满足条件的x的长度 } printf("Test case #%d\n",cas++); printf("Total explored area: %.2lf\n\n",res); } return 0; }
pku 1177 Picture 矩形周长并
http://poj.org/problem?id=1177
题意:
给出n个矩形,每个矩形给出左下角坐标,右上角坐标。然后求矩形周长的并;
思路:
扫描线,按x轴建树,len[]记录横向有效长度,numseg[]记录叔侄方向的有效个数。然后就是扫描一遍过去。
View Code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a , b) ((a) < (b) ? (a) : (b)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 10007 #define N 20007 using namespace std; //freopen("din.txt","r",stdin); struct Seg{ int l,r,h; int mk; Seg(){} Seg(int L,int R,int H,int MK){ l = L; r = R; h = H; mk = MK; } bool operator < (const Seg &tmp) const{ return h < tmp.h; } }seg[M]; int len[N<<2],cnt[N<<2],numseg[N<<2]; int lbd[N<<2],rbd[N<<2]; void pushup(int rt,int l,int r){ if (cnt[rt]){ len[rt] = r - l + 1; lbd[rt] = rbd[rt] = 1;//标记边界 numseg[rt] = 2; } else if (l == r) len[rt] = lbd[rt] = rbd[rt] = numseg[rt] = 0; else{ len[rt] = len[rt<<1] + len[rt<<1|1]; numseg[rt] = numseg[rt<<1] + numseg[rt<<1|1]; lbd[rt] = lbd[rt<<1]; rbd[rt] = rbd[rt<<1|1]; if (lbd[rt<<1|1] && rbd[rt<<1]) numseg[rt] -= 2;//边界重合重合 } } void update(int L,int R,int sc,int l,int r,int rt){ if (L <= l && r <= R){ cnt[rt] += sc; pushup(rt,l,r); return ; } int m = (l + r)>>1; if (L <= m) update(L,R,sc,lc); if (m < R) update(L,R,sc,rc); pushup(rt,l,r); } int main(){ //freopen("din.txt","r",stdin); int n,i; int x1,y1,x2,y2; scanf("%d",&n); int m = 0; int L = 10001,R = -10001; for (i = 0; i < n; ++i){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); L = min(L,x1);//求出边界 R = max(R,x2); seg[m++] = Seg(x1,x2,y1,1); seg[m++] = Seg(x1,x2,y2,-1); } sort(seg,seg + m); int res = 0; int last = 0; CL(cnt,0); CL(numseg,0); CL(len,0); CL(lbd,0); CL(rbd,0); //扫描 for (i = 0; i < m; ++i){ if (seg[i].l < seg[i].r) update(seg[i].l,seg[i].r - 1,seg[i].mk,L,R - 1,1); res += numseg[1]*(seg[i + 1].h - seg[i].h);//竖直方向上有效长度 res += iabs(len[1] - last);//水平方向上的长度 last = len[1];//last记录上次的横向的有效长度 } printf("%d\n",res); return 0; }