扫描线(面积并+周长并模板+一例)

下面两种写法一样:

class Edge{
    
    
	public:
		double x1,x2,h;
		int f;
	Edge(double a=0,double b=0,double c=0,int d=0):x1(a),x2(b),h(c),f(d){
    
    }
};
bool cmp(Edge a,Edge b){
    
    	return a.h<b.h||(a.h==b.h);	}
struct Edge{
    
    
	int x1,x2,h;
	int f;
	Edge(int a=0,int b=0,int c=0,int d=0):x1(a),x2(b),h(c),f(d){
    
    }
}e[maxn*2];
bool cmp(Edge a,Edge b){
    
    	return a.h<b.h||(a.h==b.h);	}
struct Edge{
    
    
	int x1,x2,h,f;
	Edge(int a=0,int b=0,int c=0,int d=0):x1(a),x2(b),h(c),f(d){
    
    }
	bool operator<(const Edge&t)const{
    
    	return h<t.h||(h==t.h);	}
}e[maxn];

扫描线说白了就是线段树的应用,下面都是模板
线段树详解

面积并+离散

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=100+7;
int n,cnt,sum;	//n个点,左下和右 
int x[maxn*2];			//横坐标 

struct Edge{
    
    
	int l,r;	//这条线的左右端点的横坐标
	int h;		//这条线的纵坐标
	int f;		//这条线是矩形的上边还是下边
}e[maxn*2];
struct Node{
    
    
	int l,r;	//横坐标的区间,是横坐标数组的下标
    int s;		//该节点被覆盖的情况(是否完全覆盖)
    int len;	//该区间被覆盖的总长度
}q[maxn*8];
bool cmp(Edge a,Edge b){
    
    	return a.h<b.h;		}
#define ls		i<<1
#define rs		i<<1|1
#define m(i)	((q[i].l+q[i].r)>>1)
void build(int i,int l,int r){
    
    
	q[i].l=l;	q[i].r=r;
	q[i].s=0;	q[i].len=0;
	if(l==r)	return;
	int mid=m(i);
	build(ls,l,mid);
	build(rs,mid+1,r);
}


void kk(){
    
    
	for(int i=1;i<=7;i++){
    
    
		cout<<q[i].len<<endl;
	}
}

void pushup(int i){
    
    
	//非零,已经被整段覆盖
	if(q[i].s)				q[i].len=x[q[i].r+1]-x[q[i].l];
	//这是一个点而不是线段
	else if(q[i].l==q[i].r)	q[i].len=0;
	//是一条没有整个区间被覆盖的线段,合并左右子的信息
	else					q[i].len=q[ls].len+q[rs].len;
}
void update(int i,int l,int r,int xx){
    
    	//令下边为1,上边-1,下边插入边,上边删除边
	if(q[i].l==l&&q[i].r==r){
    
    
		q[i].s+=xx;
		pushup(i);		//更新区间被覆盖de总长度
		return;
	}
	
	kk();
	
	int mid=m(i);
	if(r<=mid)		update(ls,l,r,xx);
	else if(l>mid)	update(rs,l,r,xx);
	else{
    
    
		update(ls,l,mid,xx);
		update(rs,mid+1,r,xx);
	}
	pushup(i);
	
	kk();
	
}

int main(){
    
    
	cin>>n;
	for(int i=0;i<n;i++){
    
    
		int x1,x2,y1,y2;
		cin>>x1>>y1>>x2>>y2;
		e[cnt].l=e[cnt+1].l=x1;
		e[cnt].r=e[cnt+1].r=x2;
		e[cnt].h=y1;	e[cnt].f=1;
		e[cnt+1].h=y2;	e[cnt+1].f=-1;
		x[cnt]=x1;	x[cnt+1]=x2;
		cnt+=2;
	}
	sort(e,e+cnt,cmp);	//边按高度从小到大排序(自下而上扫描)
	sort(x,x+cnt);		//离散化横坐标
	int k=unique(x,x+cnt)-x;
//	int k=1;
//	for(int i=1;i<cnt;i++)	if(x[i]!=x[i-1])	x[k++]=x[i];	//去重 
//	cout<<k<<endl;
	build(1,0,k-1);
	for(int i=0;i<cnt;i++){
    
    
		//因为线段树维护的是横坐标们的下标,所以对每条边求出其两个横坐标对应的下标
		int l=lower_bound(x,x+k,e[i].l)-x;	//在横坐标数组里找到这条边的位置
		int r=lower_bound(x,x+k,e[i].r)-x-1;
		
		cout<<l<<' '<<r<<endl;
		
		update(1,l,r,e[i].f);				//每扫到一条边就更新横向的覆盖len
		sum+=(e[i+1].h-e[i].h)*q[1].len;	//q[1]是整个区间,q[1].k=len是整个区间的有效长度
		
		cout<<q[1].len<<endl;
		
		//计算面积就是用区间横向的有效长度乘以两条边的高度差(面积是两条边里面的部分)
	}
	cout<<"面积并为:"<<sum<<endl;
}

/*
2
10 10 20 20
15 15 25 25

175
*/

周长并

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 const int inf=0x3f3f3f3f;
 
int n,cot,Sum,pre,Left,Right;
const int maxn=100+7;

struct Edge{
    
    
	int x1,x2,h;
	int f;
	Edge(int a=0,int b=0,int c=0,int d=0):x1(a),x2(b),h(c),f(d){
    
    }
}e[maxn*2];
bool cmp(Edge a,Edge b){
    
    	return a.h<b.h||(a.h==b.h&&a.f>b.f);	}
//这里一定不能去掉,比如这组数据:2   0 0 4 4    0 4 4 8--->多算中间的一条边两边,先合并再舍去
#define ls		i<<1
#define rs		i<<1|1
#define m(i)	((l+r)>>1)
#define lson 	ll,rr,xx,l,mid
#define rson 	ll,rr,xx,mid+1,r
int len[maxn<<2],cover[maxn<<2],cnt[maxn<<2];		//区间覆盖的长度,覆盖的次数,竖线的条数
bool fl[maxn*4],fr[maxn*4];				//左端点是否被覆盖,右端点是否被覆盖

//struct node{
    
    
//	int len,cover,cnt;		//区间覆盖的长度,覆盖的次数,竖线的条数
//	bool fl,fr;				//左端点是否被覆盖,右端点是否被覆盖
//}q[maxn*8];

void pushup(int i,int l,int r){
    
    
	if(cover[i]){
    
    
		len[i]=r-l+1;
		fl[i]=fr[i]=1;
		cnt[i]=2;		//该区间被一条底边全部覆盖,可用竖边为2 
	}
	else if(l==r)	len[i]=fl[i]=fr[i]=cnt[i]=0;
	else{
    
    
		len[i]=len[ls]+len[rs];
		cnt[i]=cnt[ls]+cnt[rs];
		fl[i]=fl[ls];
		fr[i]=fr[rs];
		if(fr[ls]&&fl[rs])		cnt[i]-=2;		//左右区间连接
	}
}

void update(int i,int ll,int rr,int xx,int l,int r){
    
    
	if(l>=ll&&r<=rr){
    
    
		cover[i]+=xx;
		pushup(i,l,r);
		return;
	}
	int mid=m(i);
	if(ll<=mid)		update(ls,lson);
	if(rr>mid)		update(rs,rson);
	pushup(i,l,r);
}
int main(){
    
    
	while(cin>>n){
    
    
		Left=inf;	Right=-inf;
		Sum=pre=cot=0;
		for(int i=0;i<n;i++){
    
    
			int x1,x2,y1,y2;
			cin>>x1>>y1>>x2>>y2;
			e[cot]=Edge(x1,x2,y1,1);
			e[cot+1]=Edge(x1,x2,y2,-1);
			cot+=2;
			Left=min(Left,x1);
			Right=max(Right,x2);
		}
		sort(e,e+cot,cmp);
		for(int i=0;i<cot;i++){
    
    
			int l=e[i].x1;
			int r=e[i].x2;
			if(l<r)		update(1,l,r-1,e[i].f,Left,Right);
			Sum+=cnt[1]*(e[i+1].h-e[i].h);
			Sum+=abs(len[1]-pre);
			pre=len[1];
		}
		cout<<Sum<<endl;
	}
}


/*
7 
-15 0 5 10 
-5 8 20 25 
15 -4 24 14 
0 -6 16 4 
2 15 10 22 
30 10 36 20 
34 0 40 16

228


*/

求覆盖两次以上的面积并

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e3+7;
int n,cot,T;
double Sum;
double x[maxn*2];
int cnt[maxn*8];
double sum1[maxn*8],sum2[maxn*8];
class Edge{
    
    
	public:
		double x1,x2,h;
		int f;
	Edge(double a=0,double b=0,double c=0,int d=0):x1(a),x2(b),h(c),f(d){
    
    }
};
bool cmp(Edge a,Edge b){
    
    	return a.h<b.h||(a.h==b.h&&a.f<b.f);	}
Edge e[maxn*2];

#define ls		i<<1
#define rs		i<<1|1
#define m(i)	((l+r)>>1)
#define eps 1e-15
void build(int i,int l,int r){
    
    
	cnt[i]=sum1[i]=sum2[i]=0;
	if(l==r)	return;
	int mid=m(i);
	build(ls,l,mid);
	build(rs,mid+1,r);
}
void pushup1(int i,int l,int r){
    
    
	if(cnt[i])				sum1[i]=x[r+1]-x[l];
	else if(l==r)			sum1[i]=0;
	else					sum1[i]=sum1[ls]+sum1[rs];
}
void pushup2(int i,int l,int r){
    
    
	if(cnt[i]>1)			sum2[i]=x[r+1]-x[l];
	else if(l==r)			sum2[i]=0;
	else if(cnt[i]==1)		sum2[i]=sum1[ls]+sum1[rs];
	else					sum2[i]=sum2[ls]+sum2[rs];
}
void update(int i,int ll,int rr,int xx,int l,int r){
    
    
	if(l>=ll&&r<=rr){
    
    
		cnt[i]+=xx;
		pushup1(i,l,r);
		pushup2(i,l,r);
		return;
	}
	
	int mid=m(i);
	if(ll<=mid)		update(ls,ll,rr,xx,l,mid);
	if(rr>mid)		update(rs,ll,rr,xx,mid+1,r);
	pushup1(i,l,r);
	pushup2(i,l,r);
}
int main(){
    
    
	scanf("%d",&T);
	while(T--){
    
    
		scanf("%d",&n);
		Sum=cot=0;
		for(int i=0;i<n;i++){
    
    
			double x1,x2,y1,y2;
			scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
			e[cot]=Edge(x1,x2,y1,1);
			e[cot+1]=Edge(x1,x2,y2,-1);
			x[cot]=x1;	x[cot+1]=x2;
			cot+=2;
		}
		sort(e,e+cot,cmp);
		sort(x,x+cot);
		int k=unique(x,x+cot)-x;
		build(1,0,k-1);
		int l=lower_bound(x,x+k,e[0].x1)-x;	
		int r=lower_bound(x,x+k,e[0].x2)-x;
		update(1,l,r-1,e[0].f,0,cot);
		for(int i=1;i<cot;i++){
    
    
			Sum+=(e[i].h-e[i-1].h)*sum2[1];
			l=lower_bound(x,x+k,e[i].x1)-x;	
			r=lower_bound(x,x+k,e[i].x2)-x;
			update(1,l,r-1,e[i].f,0,cot);
		}
		printf("%.2lf\n",Sum);
	}
}

/*
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1


7.63
0.00



*/ 

写多了后,扫描线的格式基本不变,pushup函数根据题意变换

猜你喜欢

转载自blog.csdn.net/weixin_45606191/article/details/107523336
今日推荐