线段树相关

发布时间 2023-04-10 10:56:58作者: lrxQwQ

一、前言

二、扫描线

就是维护矩形的面积/周长并。

1.面积并

用一条线从下往上扫,将所有矩形变成一片一片的 ( 感性理解 ) ,容易知道最多 2*n-1 片,每片的贡献是 当前线段总长度*这片的厚度。

最多 2*n 条竖直的线,所以最多 2*n 个端点,最多 2*n-1 个区间。

离散化后计算出不同端点的个数 cnt 。线段树上 [l,r] 的区间代表实际上第 l 个端点到第 r+1 个端点。维护每一个区间的长度。

时间复杂度:O(nlogn)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=0;ch=getchar();}
    while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return f?x:-x;
}
ll ans=0;
int n,t[2000010],p[2000010],q[2000010],u[2000010],cnt;
struct scl{
	int l,r,x,h;//区间的左端点、右端点、纵坐标、这条边是矩形的下边(h=1)还是上边(h=-1)
}s[500010];
bool cmp(scl _1,scl _2){//按照纵坐标排好序
	return _1.x<_2.x;
}
void modify(int x,int l,int r,int ql,int qr,int v){
	if(ql<=l&&r<=qr){
		p[x]+=v;//区间被覆盖的次数
		t[x]=p[x]?(q[r+1]-q[l]):(t[x<<1]+t[x<<1|1]);//如果区间被完全覆盖了≥1次,该区间的线段长度就是区间总长;
		return;
	}
	int mid=(l+r)>>1;
	if(ql<=mid)modify(x<<1,l,mid,ql,qr,v);
	if(qr>mid)modify(x<<1|1,mid+1,r,ql,qr,v);
	t[x]=p[x]?(q[r+1]-q[l]):(t[x<<1]+t[x<<1|1]);
}
int main(){
	n=read();
	for(int i=1,a,b,c,d;i<=n;i++){
		a=read();b=read();c=read();d=read();
		s[(i<<1)-1]=scl{a,c,b,1};
		s[i<<1]=scl{a,c,d,-1};
		q[(i<<1)-1]=a;q[i<<1]=c;//q 数组用于存储区间端点以方便进行离散化
	}
	sort(q+1,q+2*n+1);
	cnt=unique(q+1,q+2*n+1)-q-1;
	sort(s+1,s+2*n+1,cmp);
	for(int i=1,x,y;i<2*n;i++){
		x=lower_bound(q+1,q+cnt+1,s[i].l)-q;
		y=lower_bound(q+1,q+cnt+1,s[i].r)-q;
		modify(1,1,cnt-1,x,y-1,s[i].h);
		ans+=1ll*t[1]*(s[i+1].x-s[i].x);
	}
	cout<<ans<<'\n';
	return 0;
}