线段树——扫描线
扫描线
用于求矩形面积并,矩形周长并
矩形面积并
洛谷P5490
图一
以下图片来自洛谷题解(难看的就是我自己画的qwq)
图二
以上是两个矩形,他们的面积并就是他们的面积取并
图三
考虑把这两个矩形的面积并转化成 A B C 三个矩形的面积(即把重叠问题转化为无重叠问题)
图四
如何求 A B C 的面积?
高是好求的,把四个高依次相减,就是 A B C 的高
如何求底 ?
如图四,我们把从左到右的三条线段记作 p1 p2 p3 ,那么矩形 A 的底即为 p1+p2 , 矩形 B 的底即为 p1+p2+p3 , 矩形 C 的底即为 p2+p3
那么求 A B C 的底 ,实际上就是判断什么时候用 p1 p2 p3 计算
扫描线
扫描线其实就是用来求底的
此时需要引入一个概念:出边入边
一个矩形的下底就是一条入边,一个矩形的上底就是一条出边
定义 T1 T2 T3 判断是否用 p1 p2 p3 计算当前底 , 当扫描线遇到入边时 , T + 1 , 当扫描线遇到出边时 , T - 1 , T > 0 , 则说明它对应的 P 应被计算
线段树
还是这个图 , 因为线段树的一个节点表示的是一个区间范围 , 而底的计算就是计算 p1 p2 p3 的组合 , 相当于是区间和
我们把 p1 p2 p3 看作叶子节点 , 当扫描线扫到一条入边时 , 则向区间加这条线段 ;当扫描线扫到一条出边时 ,则向区间减这条线段
复杂度 O(mlogn)
看不懂没关系 , 来看看代码吧 !
代码
步骤:
①读取所有矩形,记录入边出边
struct node{
ll xl,xr,y;//边的左x坐标,右x坐标, y坐标
ll v;//记录是入边(+1)还是出边(-1)
bool operator < (const node &x) const{
return y<x.y;
}
}s[N<<1];
ll cnt=0;
while(n--){
ll x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
s[++cnt]=(node){x1,x2,y1,1};
x[cnt]=x1;
s[++cnt]=(node){x1,x2,y2,-1};
x[cnt]=x2;
}
②对所有边按 y 轴排序(扫描线是从下往上依次扫的)
sort(s+1,s+cnt+1);//上面有重载运算符
③对 x 轴离散化(看看数据范围)
sort(x+1,x+cnt+1);
int num=unique(x+1,x+cnt+1)-(x+1);
ll ans=0;
for(int i=1;i<cnt;i++){
int L,R;
L=lower_bound(x+1,x+num+1,s[i].xl)-x;
R=lower_bound(x+1,x+num+1,s[i].xr)-x;
④更新线段树
//应该不用建树吧 ,emmm……,也没有啥好初始的吧
void pushup(ll p,ll pl,ll pr){
if(tag[p]){//如果T>0
lenth[p]=x[pr]-x[pl]; //计算宽度
}
else if(pl+1==pr)lenth[p]=0; //叶子节点(这里的叶子节点是一个一个块,详见图四)
else lenth[p]=lenth[ls(p)]+lenth[rs(p)];
}
void update(ll p,ll L,ll R,ll pl,ll pr,ll d){
if(L<=pl&&R>=pr){
tag[p]+=d;
pushup(p,pl,pr);
return;
}
if(pl+1==pr)return;
int mid=(pl+pr)>>1;
if(L<=mid) update(ls(p),L,R,pl,mid,d);
if(R>mid) update(rs(p),L,R,mid,pr,d);//注意不是mid+1,理由同上
pushup(p,pl,pr);
}
update(1,L,R,1,num,s[i].v);
⑤面积求和
ans+=lenth[1]*(s[i+1].y-s[i].y);
总代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100005
ll ls(int x){return x<<1;}
ll rs(int x){return x<<1|1;}
struct node{
ll xl,xr,y;
ll v;
bool operator < (const node &x) const{
return y<x.y;
}
}s[N<<1];
ll x[N<<1],tag[N<<2],lenth[N<<1];
void pushup(ll p,ll pl,ll pr){
if(tag[p]){
lenth[p]=x[pr]-x[pl];
}
else if(pl+1==pr)lenth[p]=0;
else lenth[p]=lenth[ls(p)]+lenth[rs(p)];
}
void update(ll p,ll L,ll R,ll pl,ll pr,ll d){
if(L<=pl&&R>=pr){
tag[p]+=d;
pushup(p,pl,pr);
return;
}
if(pl+1==pr)return;
int mid=(pl+pr)>>1;
if(L<=mid) update(ls(p),L,R,pl,mid,d);
if(R>mid) update(rs(p),L,R,mid,pr,d);
pushup(p,pl,pr);
}
int main(){
int n;
ll cnt=0;
cin>>n;
while(n--){
ll x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
s[++cnt]=(node){x1,x2,y1,1};
x[cnt]=x1;
s[++cnt]=(node){x1,x2,y2,-1};
x[cnt]=x2;
}
sort(s+1,s+cnt+1);
sort(x+1,x+cnt+1);
int num=unique(x+1,x+cnt+1)-(x+1);
ll ans=0;
for(int i=1;i<cnt;i++){
int L,R;
L=lower_bound(x+1,x+num+1,s[i].xl)-x;
R=lower_bound(x+1,x+num+1,s[i].xr)-x;
update(1,L,R,1,num,s[i].v);
ans+=lenth[1]*(s[i+1].y-s[i].y);
}
cout<<ans;
return 0;
}