传送门
求面积的交.
做法应该不难想到,用扫描线维护被覆盖了两次的线段长度.
面积的并的做法应该不陌生.维护覆盖一次的线段长度就ok了.没做过的可以看这篇博客的后面关于扫描线算法的讲解.
扫描线使用的线段树和平时使用的线段树有一个比较特殊的地方.维护矩形的时候总是有一个相同范围的入边和出边.所以我们可以不打lazy标记,直接只对[l,r]区间操作.打了lazy标记反而会不太好.因为覆盖的长度是底层往上统计的.如果打lazy标记的话无法得到正确的答案.要是想得到正确的答案的话就要遍历所有跟节点了.复杂度也就不优秀了.变成了O(n^2)了.所以一般不打lazy标记.(这只是我对扫描线的一点拙见…毕竟也就只写过5.6题…说的不对望指正)
所以这题的做法也一样,不打lazy标记.只统计覆盖次数就好了.同时把len变量换成两个len1,len2记录被覆盖一次和两次的线段长度.这样子就可以直接统计长度了.这个区间如果只被覆盖一次,那么它被覆盖两次的长度就是左右区间被覆盖一次长度的和.
代码
#pragma GCC optimize(2)
#define LL long long
#define pq priority_queue
#define ULL unsigned long long
#define pb push_back
#define mem(a,x) memset(a,x,sizeof a)
#define pii pair<int,int>
#define fir(i,a,b) for(int i=a;i<=b;++i)
#define afir(i,a,b) for(int i=a;i>=b;--i)
#define ft first
#define vi vector<int>
#define sd second
#define L 2*i
#define R 2*i+1
#define ALL(a) a.begin(),a.end()
#define bug puts("--------")
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
inline int read(){
int x = 0,f=1;char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Point{
double x,y1,y2;
int k;
bool operator<(const Point&w)const{
if(x == w.x) return k > w.k;
return x < w.x;
}
}p[N*2];
struct Tree{
int l,r,cnt,lz;
double len1,len2;
}tree[N*4];
vector<double> all;
int getpos(double x){
return lower_bound(ALL(all),x) - all.begin();
}
void getlen(int i){
if(tree[i].cnt >= 2) tree[i].len2 = all[tree[i].r+1] - all[tree[i].l];
else if(tree[i].cnt == 1) tree[i].len2 = tree[L].len1 + tree[R].len1;
else tree[i].len2 = tree[L].len2 + tree[R].len2;
if(tree[i].cnt >= 1) tree[i].len1 = all[tree[i].r+1]-all[tree[i].l];
else tree[i].len1 = tree[L].len1 + tree[R].len1;
}
void build(int i,int l,int r){
tree[i] = {l,r,0,0,0,0};
if(l >= r) return;
int mid = l + r >> 1;
build(L,l,mid);
build(R,mid+1,r);
}
void change(int i,int l,int r,int v){
if(tree[i].l >= l && tree[i].r <= r){
tree[i].cnt += v;
getlen(i);
return;
}
int mid = tree[i].l + tree[i].r >> 1;
if(r <= mid) change(L,l,r,v);
else if(l > mid) change(R,l,r,v);
else{
change(L,l,mid,v);
change(R,mid+1,r,v);
}
getlen(i);
}
int main(){
int n,t;
scanf("%d",&t);
fir(i,1,t){
scanf("%d",&n);
int cnt = 0;
all.clear();
all.pb(-1);
fir(i,1,n){
double x1,x2,y1,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
all.pb(x1);all.pb(x2);all.pb(y1);all.pb(y2);
p[++cnt] = {x1,y1,y2,1};
p[++cnt] = {x2,y1,y2,-1};
}
sort(p+1,p+1+cnt);
sort(ALL(all));
all.erase(unique(ALL(all)),all.end());
build(1,1,(int)all.size()-1);
double ans = 0;
p[0].x = p[1].x;
fir(i,1,cnt){
int y1 = getpos(p[i].y1),y2 = getpos(p[i].y2);
ans += tree[1].len2*(p[i].x-p[i-1].x);
change(1,y1,y2-1,p[i].k);
}
printf("%.2lf\n",ans);
}
return 0;
}