#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1010;
int n;
double X[maxn<<1];
struct Edge{
double l,r,h;
int v;
Edge(){};
Edge(double a,double b,double c,int d):l(a),r(b),h(c),v(d){};
bool operator<(const Edge &b)const{
if(h==b.h)return v>b.v;
return h<b.h;
}
}edge[maxn<<1];
struct node{
int l,r,cnt;
double s,ss;
}ST[maxn<<3];
void build(int l,int r,int rt){
ST[rt].l=l;ST[rt].r=r;
ST[rt].cnt=ST[rt].s=ST[rt].ss=0;
if(l==r)return;
int m=l+r>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
void cal(int l,int r,int rt){
if(ST[rt].cnt){
ST[rt].s=X[r+1]-X[l];
}else if(ST[rt].l==ST[rt].r){
ST[rt].s=0;
}else{
ST[rt].s=ST[rt<<1].s+ST[rt<<1|1].s;
}
///
if(ST[rt].cnt>1){
ST[rt].ss=X[r+1]-X[l];
}else if(l==r){
ST[rt].ss=0;
}else if(ST[rt].cnt==1){
ST[rt].ss=ST[rt<<1].s+ST[rt<<1|1].s;
}else{
ST[rt].ss=ST[rt<<1].ss+ST[rt<<1|1].ss;
}
}
void update(int a,int b,int v,int l,int r,int rt){
if(a<=l&&b>=r){
ST[rt].cnt+=v;
cal(l,r,rt);
return;
}
int m=l+r>>1;
if(a<=m)update(a,b,v,l,m,rt<<1);
if(b>m)update(a,b,v,m+1,r,rt<<1|1);
cal(l,r,rt);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int tt=0;
for(int i=1;i<=n;i++){
double x1,x2,y1,y2;
scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
X[++tt]=x1;
edge[tt]=Edge(x1,x2,y1,1);
X[++tt]=x2;
edge[tt]=Edge(x1,x2,y2,-1);
}
sort(X+1,X+tt+1);
sort(edge+1,edge+tt+1);
int m=1;
for(int i=2;i<=tt;i++){
if(X[i]!=X[i-1])X[++m]=X[i];
}
build(1,m,1);
double ans=0;
for(int i=1;i<tt;i++){
int l=lower_bound(X+1,X+m+1,edge[i].l)-X;
int r=lower_bound(X+1,X+m+1,edge[i].r)-X-1;
update(l,r,edge[i].v,1,m,1);
ans+=ST[1].ss*(edge[i+1].h-edge[i].h);
}
printf("%.2lf\n",ans);
}
}
大部分代码风格能和扫描线保持一致还蛮开心的。。
多次覆盖的核心就在于cal函数
若当前区间被覆盖过一次,当前区间被覆盖2次的长度就加上子节点们覆盖1次的长度
当时听学长讲的时候还感觉很迷,当时光听,没分清楚覆盖次数和覆盖长度。。orz
估计判AC的时候给了eps吧。。不然我样例都没过啊(