题目描述
求 n(n≤100) n ( n ≤ 100 ) 个矩形的面积并。
吐槽
此题十分玄学,开始敲了一份代码,调了一晚上不过,返回 MLE
。
今天上午继续调试,发现网上的代码开 G++ 过不去,发现原来是卡 %lf
,改成 %f
网上的代码就能过了,对拍了半天拍不出错,肉眼查出 query(int x)
那里应该是 query(double x)
,不然会 RE
做了很多简化之后在 入门OJ 上过了,但在 POJ 上 WA
,去掉简化的内容就在 POJ 上过了,心累。
算法分析
离散化后使用扫描线,从左向右(也可以从上到下)扫描矩形与扫描线平行的边,用线段树维护目前的覆盖情况。
这个线段树不是普通的线段树,普通线段树貌似搞不了,我们发现,对于区间的增减总是成对出现,那么这里的线段树就是仅仅用来将线段拆分成 log2n l o g 2 n 段以降低时间复杂度的,因此不必下传标记,只要记录当前段被覆盖的次数,维护当前区间内被覆盖的长度即可。
代码实现
#include <cstdio>
#include <algorithm>
const int maxn=105;
struct ask {
double t,x,l,r;
bool operator < (const ask &rhs) const {
return x<rhs.x;
}
} asks[2*maxn];
double hash[2*maxn];int idx=0;
inline int query(double x) {return std::lower_bound(hash,hash+idx,x)-hash;}
namespace SGT {
int n,cnt[4*2*maxn];double ans[4*2*maxn];
void build(int o,int l,int r) {
if(l==r) ans[o]=cnt[o]=0;
else {
int mid=(l+r)>>1;
build(o<<1,l,mid);build(o<<1|1,mid+1,r);
ans[o]=cnt[o]=0;
}
}
inline void init(int N) {n=N;build(1,1,n);}
int ql,qr;
void add(int o,int l,int r) {
if(ql<=l&&r<=qr) {
++cnt[o];
ans[o]=hash[r]-hash[l-1];
}
else {
int mid=(l+r)>>1;
if(ql<=mid) add(o<<1,l,mid);
if(mid+1<=qr) add(o<<1|1,mid+1,r);
if(!cnt[o]) ans[o]=ans[o<<1]+ans[o<<1|1];
}
}
inline void add(int l,int r) {ql=l;qr=r;add(1,1,n);}
void clear(int o,int l,int r) {
if(ql<=l&&r<=qr) {
--cnt[o];
if(!cnt[o]) ans[o]=ans[o<<1]+ans[o<<1|1];
}
else {
int mid=(l+r)>>1;
if(ql<=mid) clear(o<<1,l,mid);
if(mid+1<=qr) clear(o<<1|1,mid+1,r);
if(!cnt[o]) ans[o]=ans[o<<1]+ans[o<<1|1];
}
}
inline void clear(int l,int r) {ql=l;qr=r;clear(1,1,n);}
inline double query() {return ans[1];}
}
int main() {
int n,kase=0;
while(scanf("%d",&n)==1&&n) {
idx=0;double x1,y1,x2,y2;
for(int i=0;i<n;++i) {
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
asks[i]=(ask){1,x1,y1,y2};asks[n+i]=(ask){-1,x2,y1,y2};
hash[idx++]=y1;hash[idx++]=y2;
}
std::sort(hash,hash+idx);idx=std::unique(hash,hash+idx)-hash;
std::sort(asks,asks+2*n);
SGT::init(idx);
double ans=0,last=0;
for(int i=0;i<2*n;++i) {
int l=query(asks[i].l)+1,r=query(asks[i].r);
ans+=SGT::query()*(asks[i].x-last);
if(asks[i].t==1) SGT::add(l,r);
else if(asks[i].t==-1) SGT::clear(l,r);
last=asks[i].x;
}
if(kase) printf("\n");
printf("Test case #%d\nTotal explored area: %.2f\n",++kase,ans);
}
return 0;
}