【C++】矩形面积并、矩形面积交、矩形周长并(离散化、扫描线、线段树总结)

一.矩形面积并

题目:Atlantis

POJ-1151 HDU-1542 LA-2184 ZOJ-1128
链接:https://vjudge.net/problem/HDU-1542

在这里插入图片描述

方法一:离散化

浮点无法建树, 离散化坐标就可以了。

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))

using namespace std;

typedef long long ll;

int const N=105;

int n,m,n1,n2;
int vis[N<<1][N<<1];
double ans;
double x[N],y[N],x2[N],y2[N],tx[N<<1],ty[N<<1];

int main() {
	int cas=0;
	while(scanf("%d",&n)!=EOF && n) {
		memset(vis,0,sizeof(vis));	
		for(int i=1; i<=n; i++) {
			scanf("%lf%lf%lf%lf",&x[i],&y[i],&x2[i],&y2[i]);
			tx[2*i-1]=x[i];
			tx[2*i]=x2[i];
			ty[2*i-1]=y[i];
			ty[2*i]=y2[i];
		}
		sort(tx+1,tx+2*n+1);
		sort(ty+1,ty+2*n+1);
		int tn=unique(tx+1,tx+2*n+1)-tx-1;
		int tm=unique(ty+1,ty+2*n+1)-ty-1;
		for(int i=1; i<=n; i++) {
			int t1=lower_bound(tx+1,tx+tn+1,x[i])-tx;
			int t2=lower_bound(tx+1,tx+tn+1,x2[i])-tx;
			int p1=lower_bound(ty+1,ty+tm+1,y[i])-ty;
			int p2=lower_bound(ty+1,ty+tm+1,y2[i])-ty;
			for(int j=t1; j<t2; j++) for(int k=p1; k<p2; k++) vis[j][k]=1;
		}
		ans=0;
		for(int i=1; i<tn; i++) for(int j=1; j<tm; j++) 
			if(vis[i][j]) ans+=(tx[i+1]-tx[i])*(ty[j+1]-ty[j]);
		printf("Test case #%d\n",++cas);
		printf("Total explored area: %0.2f\n\n",ans);
	}
	return 0; 
}

方法二:扫描线

首先把矩形按轴分成两条边, 上边和下边, 对轴建树, 扫描线可以看成一根平行于轴的直线. 从开始往上扫, 下边表示要计算面积, 上边表示已经扫过了, 直到扫到最后一条平行于轴的边。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))

using namespace std;

typedef long long ll;

int const N=105;

int n,m,n1,n2;
int vis[N<<1];
double ans;
double x[N],y[N],x2[N],y2[N],tx[N<<1],ty[N<<1];

struct line {
    double x,y1,y2;
    int v;
    bool operator < (const line &rhs) const {
        return x<rhs.x;
    }
} a[N<<1];

inline void updata(int t1,int t2,int v) {
    for(int i=t1; i<t2; i++) vis[i]+=v;
}

int main() {
    int cas=0;
    while(scanf("%d",&n)!=EOF && n) {
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=n; i++) {
            scanf("%lf%lf%lf%lf",&x[i],&y[i],&x2[i],&y2[i]);
            tx[(i<<1)-1]=x[i];
            tx[(i<<1)]=x2[i];
            ty[(i<<1)-1]=y[i];
            ty[(i<<1)]=y2[i];
            a[(i<<1)-1]=(line){x[i],y[i],y2[i],1};
            a[(i<<1)]=(line){x2[i],y[i],y2[i],-1};
            //此处line的括号可加可不加,原则上都是对的,有些编译器加了过不了,有些编译器没加过不了,自己可以相应调整。
        }
        sort(a+1,a+(n<<1|1));
        sort(tx+1,tx+(n<<1|1));
        sort(ty+1,ty+(n<<1|1));
        int tn=unique(tx+1,tx+(n<<1|1))-tx-1;
        int tm=unique(ty+1,ty+(n<<1|1))-ty-1;
        ans=0;
        int k=1;
        for(int i=1; i<tn; i++) {
            while(k<=(n<<1) && a[k].x<=tx[i]) {
                int t1=lower_bound(ty+1,ty+tm+1,a[k].y1)-ty;
                int t2=lower_bound(ty+1,ty+tm+1,a[k].y2)-ty;
                updata(t1,t2,a[k++].v);
            }
            double dy=0;
            for(int j=1; j<tm; j++) 
                if(vis[j]) dy+=ty[j+1]-ty[j];
            ans+=dy*(tx[i+1]-tx[i]);
        }
        printf("Test case #%d\n",++cas);
        printf("Total explored area: %0.2f\n\n",ans);
    }
    return 0; 
}

方法三:线段树+扫描线

线段树用于动态维护扫描线在往上走时, 轴哪些区域是有合法面积的。
line 就是扫描线,是一条竖线。
按照x排序。
v表示+1 还是-1
a数组就有2*n扫描线。
对扫描线进行排序。
枚举每个dx,for(int i=1;i<tn;i++){}
tx[i+1]到tx[i]就是dx
while 循环是为了把竖线添加进去。
取决a[k].v 。
我们统计竖线里面有多少长度被覆盖了。
for(int j=1; j<tm; j++) {} 统计dy
线段树就是维护一个区间覆盖。
时间复杂度做到O(N * Log N)。

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define lch                (x<<1)
#define rch                (x<<1|1)
#define mid                (l+r)/2

using namespace std;

typedef long long ll;

int const N=105;

int n;
int c[N<<4];
//c[]表示被覆盖了几次
double ans;
double x[N],y[N],x2[N],y2[N],tx[N<<1],ty[N<<1],sum[N<<4];
//sum[]表示区间和

struct line {
    double x,y1,y2;
    int v;
    bool operator < (const line &rhs) const {
        if(abs(x-rhs.x)>1e-6) return x<rhs.x;
            else return v>rhs.v;
    }
} a[N<<1];

inline void updata(int x,int l,int r,int ll,int rr,int v) {
    if(ll<=l && r<=rr) {
        c[x]+=v;
        if(c[x]) sum[x]=ty[r+1]-ty[l];
            else if(l==r) sum[x]=0;
                else sum[x]=sum[lch]+sum[rch];
        return;
    }
    if(ll<=mid) updata(lch,l,mid,ll,rr,v);
    if(rr>mid) updata(rch,mid+1,r,ll,rr,v);
    if(c[x]) sum[x]=ty[r+1]-ty[l];
        else sum[x]=sum[lch]+sum[rch];
}

int main() {
    int cas=0;
    while(scanf("%d",&n)!=EOF && n) {
        for(int i=1; i<=n; i++) {
            scanf("%lf%lf%lf%lf",&x[i],&y[i],&x2[i],&y2[i]);
            tx[(i<<1)-1]=x[i];
            tx[(i<<1)]=x2[i];
            ty[(i<<1)-1]=y[i];
            ty[(i<<1)]=y2[i];
            a[(i<<1)-1]=(line){x[i],y[i],y2[i],1};
            a[(i<<1)]=(line){x2[i],y[i],y2[i],-1};
            //此处line的括号可加可不加,原则上都是对的,有些编译器加了过不了,有些编译器没加过不了,自己可以相应调整。
        }
        sort(a+1,a+(n<<1|1));
        sort(tx+1,tx+(n<<1|1));
        sort(ty+1,ty+(n<<1|1));
        int tn=unique(tx+1,tx+(n<<1|1))-tx-1;
        int tm=unique(ty+1,ty+(n<<1|1))-ty-1;
        ans=0;
        int k=1;
        memset(sum,0,sizeof(sum));
        memset(c,0,sizeof(c));
        for(int i=1; i<tn; i++) {
            while(k<=(n<<1) && a[k].x<=tx[i]) {
                int t1=lower_bound(ty+1,ty+tm+1,a[k].y1)-ty;
                int t2=lower_bound(ty+1,ty+tm+1,a[k].y2)-ty;
                updata(1,1,tm-1,t1,t2-1,a[k++].v);
            }
            ans+=sum[1]*(tx[i+1]-tx[i]);
        }
        printf("Test case #%d\n",++cas);
        printf("Total explored area: %0.2f\n\n",ans);
    }
    return 0; 
}

二.矩形面积交

题目:覆盖的面积

HDU-1255
链接:https://vjudge.net/problem/HDU-1255
在这里插入图片描述

思路

前面的与矩形面积并类似, 不同的是的时候要考虑至少覆盖一次和至少覆盖两次的更新。尤其是当前被覆盖了一次的时候, 由于没有操作, 父亲节点的信息是没有同步到儿子节点的, 这样的话就要考虑了.父亲被记录覆盖了一次, 但是如果儿子被覆盖过, 这些操作都是在这个父亲这个大区间上的, 就相当于父亲区间被覆盖了至少两次, 所以和都要更新。

代码

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))

using namespace std;

typedef long long LL;

int const N=1005;

struct node {
    int d;
    double x,y1,y2;
    node (){}
    node (double _x,int _d,double _y1,double _y2) {
        x=_x,d=_d,y1=_y1,y2=_y2;
    }
} a[N<<1];

double two[N<<3],one[N<<3],y[N<<1],x[N<<1];
int n;
int c[N<<3];

bool cmp(node ta,node tb) {
    return ta.x<tb.x;
}

void pushup(int nod,int l,int r) {
    if(c[nod]>=2) two[nod]=one[nod]=y[r+1]-y[l];
        else if(c[nod]==1) {
            if(l==r) {
                two[nod]=0;
                one[nod]=y[r+1]-y[l];
            } else {
                two[nod]=one[nod<<1]+one[nod<<1 | 1];
                one[nod]=y[r+1]-y[l];
            }
        } else {
            if(l==r) two[nod]=one[nod]=0;
                else {
                    two[nod]=two[nod<<1]+two[nod<<1 | 1];
                    one[nod]=one[nod<<1]+one[nod<<1 | 1];
                }
        }
}

void insert(int nod,int l,int r,int ll,int rr,int d) {
    if(l==ll && r==rr) {
        c[nod]+=d;
        pushup(nod,l,r);
        return;
    }
    int mid=(l+r)>>1;
    if(rr<=mid) insert(nod<<1,l,mid,ll,rr,d);
        else if(ll>mid) insert(nod<<1 | 1,mid+1,r,ll,rr,d);
            else {
                insert(nod<<1,l,mid,ll,mid,d);
                insert(nod<<1 | 1,mid+1,r,mid+1,rr,d);
            }
    pushup(nod,l,r);
}

int main() {
    int cas;
    scanf("%d",&cas);
    while(cas--) {
        scanf("%d",&n);
        for(int i=1; i<=n; i++) {
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            a[(i<<1)-1]=node(x1,1,y1,y2);
            a[(i<<1)]=node(x2,-1,y1,y2);
            y[(i<<1)-1]=y1;
            y[(i<<1)]=y2;
            x[(i<<1)-1]=x1;
            x[(i<<1)]=x2;
        }
        sort(x+1,x+2*n+1);
        sort(y+1,y+2*n+1);
        sort(a+1,a+2*n+1,cmp);
        int m=unique(y+1,y+2*n+1)-y-1;
        int k=unique(x+1,x+2*n+1)-x-1;
        ms(0,two);
        ms(0,one);
        ms(0,c);
        double ans=0;
        int st=1;
        for(int i=1; i<=k; i++) {
            ans+=(x[i]-x[i-1])*two[1];
            while(st<=(n<<1) && a[st].x<=x[i]) {
                int l=lower_bound(y+1,y+m+1,a[st].y1)-y;
                int r=lower_bound(y+1,y+m+1,a[st].y2)-y-1;
                if(l<=r) insert(1,1,m-1,l,r,a[st].d);
                st++;
            }
        }
        printf("%0.2lf\n",ans);
        //输出用printf(),不要用cout,cout有误差。
    }
    return 0;
}

三.矩形周长并

题目:Picture

HDU-1828 POJ-1177
链接:https://vjudge.net/problem/HDU-1828

在这里插入图片描述

思路

可以用类似矩形面积并的办法, 不过这次我们不乘高, 不算面积罢了。 由于周长的线会被重复覆盖, 我们每次需要和上一次的作差.。但是这样仅仅是轴的, 不过我可以再轴做一次加起来就可以了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码

#pragma GCC optimize(3,"Ofast","inline")
#pragma G++ optimize(3,"Ofast","inline")

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

#define R                  register int
#define re(i,a,b)          for(R i=a; i<=b; i++)
#define ms(i,a)            memset(a,i,sizeof(a))
#define MAX(a,b)           (((a)>(b)) ? (a):(b))
#define MIN(a,b)           (((a)<(b)) ? (a):(b))

using namespace std;

typedef long long LL;

int const N=5005;

struct node {
    int x,y1,y2;
    node (){}
    node (int _x,int _y1,int _y2) {
        x=_x,y1=_y1,y2=_y2;
    }
} a[N],b[N];

int n,ans;
int x1[N],x2[N],y1_1[N],y2[N],x[N<<1],y[N<<1],c[N<<3],num[N<<3],up[N<<3],down[N<<3];

bool cmp(node ta,node tb) {
    return ta.x<tb.x;
}

void pushup(int nod,int l,int r) {
    if(c[nod]) num[nod]=0;
        else {
            if(l==r) {
                if((up[nod]==0 && down[nod]>0) || (up[nod]>0 && down[nod]==0)) num[nod]=1;
                    else num[nod]=0;
            } else num[nod]=num[nod<<1]+num[nod<<1|1];
        } 
}

void insert(int nod,int l,int r,int p,int u,int d) {
    if(l==r) {
        up[nod]+=u;
        down[nod]+=d;
        pushup(nod,l,r);
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid) insert(nod<<1,l,mid,p,u,d);
        else insert(nod<<1|1,mid+1,r,p,u,d);
    pushup(nod,l,r);
}

void updata(int nod,int l,int r,int ll,int rr,int d) {
    if(l==ll && r==rr) {
        c[nod]+=d;
        pushup(nod,l,r);
        return;
    }
    int mid=(l+r)>>1;
    if(rr<=mid) updata(nod<<1,l,mid,ll,rr,d);
        else if(ll>mid) updata(nod<<1|1,mid+1,r,ll,rr,d);
            else {
                updata(nod<<1,l,mid,ll,mid,d);
                updata(nod<<1|1,mid+1,r,mid+1,rr,d);
            }
    pushup(nod,l,r);
}

void solve() {
    for(int i=1; i<=n; i++) {
        x[(i<<1)-1]=x1[i];
        x[(i<<1)]=x2[i];
        y[(i<<1)-1]=y1_1[i];
        y[(i<<1)]=y2[i];
        a[i]=node{x1[i],y1_1[i],y2[i]};
        b[i]=node{x2[i],y1_1[i],y2[i]};
    }
    sort(x+1,x+(n<<1|1));
    sort(y+1,y+(n<<1|1));
    sort(a+1,a+n+1,cmp);
    sort(b+1,b+n+1,cmp);
    memset(c,0,sizeof(c));
    memset(num,0,sizeof(num));
    memset(up,0,sizeof(up));
    memset(down,0,sizeof(down));
    int numx=unique(x+1,x+(n<<1|1))-x-1;
    int numy=unique(y+1,y+(n<<1|1))-y-1;
    int st=1,ed=1;
    for(int i=1; i<=numx; i++) {
        ans+=(x[i]-x[i-1])*num[1];
        while(st<=n && a[st].x<=x[i]) {
            int l=lower_bound(y+1,y+numy,a[st].y1)-y+1;
            int r=lower_bound(y+1,y+numy,a[st].y2)-y-1;
            insert(1,1,numy,l-1,1,0);
            insert(1,1,numy,r+1,0,1);
            if(l<=r) updata(1,1,numy,l,r,1);
            st++;
        }
        while(ed<=n && b[ed].x<=x[i]) {
            int l=lower_bound(y+1,y+numy,b[ed].y1)-y+1;
            int r=lower_bound(y+1,y+numy,b[ed].y2)-y-1;
            insert(1,1,numy,l-1,-1,0);
            insert(1,1,numy,r+1,0,-1);
            if(l<=r) updata(1,1,numy,l,r,-1);
            ed++;
        }
    }
}

int main() {
    while(scanf("%d",&n)!=EOF) {
        ans=0;
        for(int i=1; i<=n; i++) scanf("%d%d%d%d",&x1[i],&y1_1[i],&x2[i],&y2[i]);
        solve();
        for(int i=1; i<=n; i++) {
            swap(x1[i],y1_1[i]);
            swap(x2[i],y2[i]);
        }
        solve();
        printf("%d\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值