一.矩形面积并
题目: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;
}