下面两种写法一样:
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函数根据题意变换