扫描线+离散化+线段树HDU1255&POJ1177
首先我们要对扫描线算法建立一个形象的认识,可以参考链接。大概就是,对于某个由多个矩形覆盖重叠形成的不规则几何体的面积,可以转化为从下往上一系列小矩形,有点像垒砖,层数为线段数-1,也就是矩形数*2-1。
在这个算法里,线段树维护整个区间被覆盖的,有效的“长”,它是由一小段一小段的线段组成,我们对于每条线段添加一个flag成员变量,当该线段被某矩形的下边包含,该线段flag+1,上边则-1。如果该线的flag大于0,则说明该线段在当前高度的有效的,就把它的len赋上它原本的长度,详细的可以看代码:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <string>
#include <set>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define N 1100
#define ll long long
using namespace std;
#define lson(rt) rt<<1
#define rson(rt) rt<<1|1
struct Seg
{
double l,r,h;
int f;
Seg() {}
Seg(double a,double b,double c,int d):l(a),r(b),h(c),f(d) {}
bool operator < (const Seg &cmp) const
{
return h<cmp.h;
}
} e[N<<1];
struct node
{
int l,r;
int cnt;
double len[2];
} t[N<<3]; //线段树
double X[N*2];
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].len[1]=0;
if(l==r)
return;
int m=(t[rt].l+t[rt].r)>>1;
build(m+1,r,rson(rt));
build(l,m,lson(rt));
}
void pushdown(int rt)
{
if(t[rt].cnt)//当前的边被标记,就把当前的长度加上
t[rt].len[0]=X[t[rt].r+1]-X[t[rt].l];
else if(t[rt].l==t[rt].r)//当为一个点的时候长度为0
t[rt].len[0]=0;
else//其他情况把左右两个区间的值加上
t[rt].len[0]=t[lson(rt)].len[0]+t[rson(rt)].len[0];
if(t[rt].cnt>1){
t[rt].len[1]=X[t[rt].r+1]-X[t[rt].l];
}else if(t[rt].l==t[rt].r)
t[rt].len[1]=0;
else if(t[rt].cnt==1)
t[rt].len[1]=t[lson(rt)].len[0]+t[rson(rt)].len[0];
else
t[rt].len[1]=t[lson(rt)].len[1]+t[rson(rt)].len[1];
}
void update(int l,int r,int rt,int val)
{ //l,r是现有线段,rt代表了待更新的区间
if(t[rt].l==l&&t[rt].r==r)
{
t[rt].cnt+=val;//加上标记的值
pushdown(rt);//向下更新节点
return;
}
int m=(t[rt].l+t[rt].r)>>1;
if(r<=m){
update(l,r,lson(rt),val);
}else if(l>m){
update(l,r,rson(rt),val);
}else{
update(l,m,lson(rt),val);
update(m+1,r,rson(rt),val);
}
pushdown(rt);
}
int T;
int main()
{
int n;
cin>>T;
double a,b,c,d;
while(T--)
{
scanf("%d",&n);
int num=0;
for(int i=0; i<n; i++)
{
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
X[num]=a;
e[num++]=Seg(a,c,b,1);//矩形下面用1来标记吗
X[num]=c;
e[num++]=Seg(a,c,d,-1);//上面用-1来标记
}
sort(X,X+num);//用于离散化
sort(e,e+num);//把矩形的边的纵坐标从小到大排序
int m=unique(X,X+num)-X;
double ans=0;
build(0,m-1,1);
for(int i=0; i<num-1; i++)
{
int l=lower_bound(X,X+m,e[i].l)-X;//找出离散化以后的值
int r=lower_bound(X,X+m,e[i].r)-X-1;
update(l,r,1,e[i].f); //1是线段树的根,代表了解决区间是0~m-1,l~r是当前更新添加的线段
ans+=t[1].len[1]*(e[i+1].h-e[i].h);
}
printf("%.2f\n",ans);
}
return 0;
}
至于poj1177,所求由面积并变为周长。稍微改变一下维护的值就好了。
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 5001
#define lson(rt) rt<<1
#define rson(rt) rt<<1|1
int pos[N][4];
int n;
struct node{
int l,r;
int len;
int cnt;
int sum;
int lf,rf;
}t[N*4];
struct seg{
int l,r;
int h;
int f;
seg(){}
seg(int a,int b,int c,int d):l(a),r(b),h(c),f(d){}
bool operator<(seg x){
return h<x.h;
}
}e[N*2];
double dis[N*2];
void build(int rt,int l,int r){
t[rt].l=l;
t[rt].r=r;
t[rt].sum=t[rt].len=t[rt].cnt=t[rt].lf=t[rt].rf=0;
if(l==r){
return;
}
int mid=(l+r)/2;
build(lson(rt),l,mid);
build(rson(rt),mid+1,r);
}
void pushdown(int rt){
if(t[rt].cnt>0){
t[rt].len=dis[t[rt].r+1]-dis[t[rt].l];
t[rt].lf=t[rt].rf=1;
t[rt].sum=2;
}else if(t[rt].l==t[rt].r){
t[rt].len=t[rt].sum=0;
t[rt].lf=t[rt].rf=0;
}else{
t[rt].len=t[lson(rt)].len+t[rson(rt)].len;
t[rt].sum=t[lson(rt)].sum+t[rson(rt)].sum;
t[rt].lf=t[lson(rt)].lf;
t[rt].rf=t[rson(rt)].rf;
if(t[lson(rt)].rf==1&&t[rson(rt)].lf==1)
t[rt].sum-=2;
}
}
void update(int l,int r,int rt,int val){
if(t[rt].l==l&&t[rt].r==r){
t[rt].cnt+=val;
pushdown(rt);
return ;
}
int m=(t[rt].l+t[rt].r)/2;
if(r<=m){
update(l,r,lson(rt),val);
}else if(l>m){
update(l,r,rson(rt),val);
}else{
update(l,m,lson(rt),val);
update(m+1,r,rson(rt),val);
}
pushdown(rt);
}
int main(){
cin>>n;
int ans=0;
for(int i=0;i<n;i++){
scanf("%d%d%d%d",&pos[i][0],&pos[i][1],&pos[i][2],&pos[i][3]);
}
for(int i=0;i<n;i++){
e[2*i]=seg(pos[i][0],pos[i][2],pos[i][1],1);
dis[2*i]=pos[i][0];
e[2*i+1]=seg(pos[i][0],pos[i][2],pos[i][3],-1);
dis[2*i+1]=pos[i][2];
}
sort(dis,dis+2*n);
sort(e,e+n*2);
int m=unique(dis,dis+2*n)-dis;
build(1,0,m-1);
int last=0;
for(int i=0;i<2*n;i++){
int l=lower_bound(dis,dis+m,e[i].l)-dis;
int r=lower_bound(dis,dis+m,e[i].r)-dis-1;
update(l,r,1,e[i].f);
ans+=t[1].sum*(e[i+1].h-e[i].h);
ans+=abs(last-t[1].len);
last=t[1].len;
}
cout<<ans<<endl;
return 0;
}