POJ 1151 Atlantis 求矩形并(附个交)的面积 (线段树+离散化)
Atlantis
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 7646 Accepted Submission(s): 3368
The input file is terminated by a line containing a single 0. Don’t process it.
Output a blank line after each test case.
2 10 10 20 20 15 15 25 25.5 0
Test case #1 Total explored area: 180.00
给定一个矩形的左下角坐标和右上角坐标分别为:(x1,y1)、(x2,y2),对这样的一个矩形,我们构造两条线段,一条定位在x1,它在y坐标的区间是[y1,y2],并且给定一个cover域值为1;另一条线段定位在x2,区间一样是[y1,y2],给定它一个cover值为-1。根据这样的方法对每个矩形都构造两个线段,最后将所有的线段根据所定位的x从左到右进行排序。

上图中,红色的字体表示的是该线段的cover值。刚刚开始的时候,线段树上的cover值都为0,但第一根线段(x==0)插入线段树的之后,我们将线段树上的cover加上该线段的cover,那么,此时线段树上被该线段覆盖的位置上的cover的值就为1,下次再插入第二根线段(x==1)此时发现该线段所覆盖的区间内,有一部分线段树的cover为0,另有一部分为1,仔细观察,但插入第二个线段的时候,如果线段树上cover已经为1的那些区间,和现在要插入的第二根线段之间,是不是构成了并面积?还不明白?看下图,绿色部分即为插入第二根线段后得到的并面积

够清楚了吧!也就是说,我们插入某跟线段的时候,只要看该线段所在区间上的cover是否大于等于1,如果是,那么就可以将并面积值加上(目前线段的x定位 - 上一线段的x定位)*(该区间的大小)
#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , mid
#define rson rs , mid + 1 , r
#define root 1 , 1 , n
#define rt o , l , r
const int N = 1010;
const int inf = 100000000;
struct node
{
double y_down, y_up, x;
int cover;
bool flag;
} s[N*4];
struct Line
{
double x, y_down, y_up;
int flag;
bool operator<(const Line &a)const
{
return x<a.x;
}
} line[2*N];
double y[2*N];
//bool cmp(Line a,Line b){ return a.x < b.x;}
void build (int o , int l , int r)
{
s[o].x = -1;
s[o].cover = 0;
s[o].y_down = y[l];
s[o].y_up = y[r];
s[o].flag = false;
if (l+1 == r)//这里的叶子节点包含2个点,也就是一单位线段
{
s[o].flag = true;
return;
}
int mid = ( l + r ) >> 1;
build(ls,l,mid);
build(rs,mid,r);
}
double update(int o, double l, double r, double x, int flag)
{
if(r <= s[o].y_down || l >= s[o].y_up)
return 0;
if(s[o].flag)
{
if(s[o].cover > 0)
{
double ans = (x - s[o].x)*(s[o].y_up - s[o].y_down);
s[o].x = x;
s[o].cover += flag;
return ans;
}
else
{
s[o].x = x;
s[o].cover += flag;
return 0;
}
}
double ans1,ans2;
ans1 = update(ls, l, r, x, flag);
ans2 = update(rs, l, r, x, flag);
return ans1+ans2;
}
int main()
{
//freopen("in.txt","r",stdin);
int count=0,n,index;
double x1,y1,x2,y2;
while(scanf("%d", &n)!= EOF &&n)
{
index = 1;
for (int i=1; i<=n; i++)
{
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
y[index] = y1;
line[index].x = x1;
line[index].y_down = y1;
line[index].y_up = y2;
line[index].flag = 1; //1表示左边
index++;
y[index] = y2;
line[index].x = x2;
line[index].y_down = y1;
line[index].y_up = y2;
line[index].flag = -1; //-1表示右边
index++;
}
sort(y+1, y+index);
sort(line+1, line+index);
build(1, 1, index-1);
double ans=0;
for (int i=1; i<index; i++)
{
ans += update(1, line[i].y_down, line[i].y_up, line[i].x, line[i].flag);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n", ++count, ans);
}
return 0;
}
第二种方法 (是按x切割的逐块长方形面积)
该方法同样需要在线段树中定义一个cover域,表示该线段区间目前被覆盖的线段数目。另外再加一个len域,表示该区间可用于与下一线段求并面积的y坐标区间长度。然后利用简单的dp,将所有信息集中于tree[1].len上,这样便不用想第一种方法那样每次都求到叶子线段,大大节约了时间,并且代码也少了很多。
代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int l;
int r;
int cover;
double len;
};
node tree[2000];
double yy[250];
int n,len;
struct Line
{
double y_down;
double y_up;
double x;
int cover;
};
Line line[250];
int cmp(Line a,Line b){return a.x<b.x;}
int find(double x)
{
int l=0,r=len,mid;
while(l<=r)
{
mid=(l+r)/2;
if(yy[mid]==x)
return mid;
if(yy[mid]<x)
l=mid+1;
else
r=mid-1;
}
return l;
}
void build(int i,int l,int r)
{
tree[i].l=l;
tree[i].r=r;
tree[i].cover=0;
tree[i].len=0;
if(l+1==r)
return;
int mid=(l+r)/2;
build(2*i,l,mid);
build(2*i+1,mid,r);
}
void fun(int i)
{
if(tree[i].cover)
tree[i].len=yy[tree[i].r]-yy[tree[i].l]; //如果cover大于1,那么整段都可用于与下一线段求并面积
else if(tree[i].l+1==tree[i].r) //叶子线段
tree[i].len=0;
else
tree[i].len=tree[2*i].len+tree[2*i+1].len; //很简单的dp
}
void updata(int i,int l,int r,int cover)
{
if(tree[i].l>r || tree[i].r<l)
return;
if(tree[i].l>=l && tree[i].r<=r)
{
tree[i].cover+=cover;
fun(i);
return;
}
updata(2*i,l,r,cover);
updata(2*i+1,l,r,cover);
fun(i);
}
int main()
{
freopen("in.txt","r",stdin);
double x1,y1,x2,y2;
int i,m,a,b,cas=1;
freopen("in.txt","r",stdin);
while(scanf("%d",&n)==1 && n)
{
m=0;
for(i=0;i<n;i++)
{
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
yy[m]=y1;
line[m].cover=1;
line[m].x=x1;
line[m].y_down=y1;
line[m++].y_up=y2;
yy[m]=y2;
line[m].cover=-1;
line[m].x=x2;
line[m].y_down=y1;
line[m++].y_up=y2;
}
sort(yy,yy+m);
len=1;
for(i=1;i<m;i++)
{
if(yy[i-1]!=yy[i])
yy[len++]=yy[i];
}
len--;
build(1,0,len);
sort(line,line+m,cmp);
double ans=0;
printf("Test case #%d\n",cas++);
for(i=0;i<m-1;i++)
{
a=find(line[i].y_down);
b=find(line[i].y_up);
updata(1,a,b,line[i].cover);
ans+=tree[1].len*(line[i+1].x-line[i].x); //tree[1].len已经保留了整个树与line[i+1]所能求并面积的长度
}
printf("Total explored area: %0.2lf\n\n",ans);
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
#define ls ( o << 1 )
#define rs ( o << 1 | 1 )
#define lson ls , l , mid
#define rson rs , mid + 1 , r
#define root 1 , 1 , n
#define rt o , l , r
const int N = 10100;
const int inf = 100000000;
struct node
{
double y_down, y_up, x;
int cover;
bool flag;//叶子节点
} s[N*4];
struct Line
{
double x, y_down, y_up;
int flag;
bool operator<(const Line &a)const
{
return x<a.x;
}
} line[2*N];
double y[2*N];
//bool cmp(Line a,Line b){ return a.x < b.x;}
void build (int o , int l , int r)
{
s[o].x = -1;
s[o].cover = 0;
s[o].y_down = y[l];
s[o].y_up = y[r];
s[o].flag = false;
if (l+1 == r)//这里的叶子节点包含2个点,也就是一单位线段
{
s[o].flag = true;
return;
}
int mid = ( l + r ) >> 1;
build(ls,l,mid);
build(rs,mid,r);
}
double update(int o, double l, double r, double x, int flag)
{
if(r <= s[o].y_down || l >= s[o].y_up)
return 0;
if(s[o].flag)
{
if(s[o].cover >= 2)//这里修改
{
double ans = (x - s[o].x)*(s[o].y_up - s[o].y_down);
s[o].x = x;
s[o].cover += flag;
return ans;
}
else
{
s[o].x = x;
s[o].cover += flag;
return 0;
}
}
double ans1,ans2;
ans1 = update(ls, l, r, x, flag);
ans2 = update(rs, l, r, x, flag);
return ans1+ans2;
}
int main()
{
//freopen("in.txt","r",stdin);
int n,index,t;
double x1,y1,x2,y2;
scanf("%d",&t);
while(t--)
{
scanf("%d", &n);
index = 1;
for (int i=1; i<=n; i++)
{
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
y[index] = y1;
line[index].x = x1;
line[index].y_down = y1;
line[index].y_up = y2;
line[index].flag = 1; //1表示左边
index++;
y[index] = y2;
line[index].x = x2;
line[index].y_down = y1;
line[index].y_up = y2;
line[index].flag = -1; //-1表示右边
index++;
}
sort(y+1, y+index);
sort(line+1, line+index);
build(1, 1, index-1);
double ans=0;
for (int i=1; i<index; i++)
{
ans += update(1, line[i].y_down, line[i].y_up, line[i].x, line[i].flag);
}
printf("%.2f\n",ans);
}
return 0;
}

浙公网安备 33010602011771号