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线段的长度。


线段树——扫描线_线段树_02线段树——扫描线_离散化_03View 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[]记录叔侄方向的有效个数。然后就是扫描一遍过去。


线段树——扫描线_线段树_02线段树——扫描线_离散化_03View 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;
}