BZOJ1382:[Baltic2001]Mars Maps
浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=1382
扫描线模板题。假设有一条直线从左往右扫过平面,那么每一单位时间内这条直线被矩形覆盖的长度的和就是矩形们的面积。而直线上被覆盖的长度只会在经过矩阵的竖边时发生变化,当我们扫到一条矩阵的左边时就在该左边所覆盖的区间上打标记表示这一段区间已经被覆盖,扫到右边时减掉。一共有\(2*n\)条边,也就一共有\(2*n-1\)个区间,每个区间的面积就是当前直线上被覆盖的长度乘以构成这个区间的两条线段的\(x\)轴差值。
我们将\(2*n\)条边按\(x\)轴坐标排序,每条线段的\(y\)轴坐标离散化,然后用线段树维护每个\(y\)轴上的区间被多少条线段覆盖了。如果区间[\(l,r\)](意思是第\(l\)条线段到第\(r+1\)条线段构成的区间)被覆盖的次数大于\(0\),那么这一段的线段被覆盖的长度就是\(y[r+1]-y[l]\),否则就由两个儿子节点更新过来。因为边都是成对出现的,所以覆盖标记不用下传。
每次直接用根节点的覆盖长度乘以两条线段的\(x\)坐标的差值累加面积即可。
时间复杂度:\(O(nlogn)\)
空间复杂度:\(O(n)\)
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=2e4+5;
ll ans;
int n,cnt;
int tmp[maxn];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
struct line {
int x,st,ed,mark;
line() {}
line(int _x,int _st,int _ed,int _mark) {
x=_x,st=_st,ed=_ed,mark=_mark;
}
bool operator<(const line &a)const {
return x<a.x;
}
}p[maxn];
struct segment_tree {
int len[maxn<<2],cnt[maxn<<2];
void updata(int p,int l,int r) {
if(cnt[p])len[p]=tmp[r+1]-tmp[l];//如果有被线段覆盖就直接加进全部长度
else if(l==r)len[p]=0;//如果是叶子节点就等于0,
else len[p]=len[p<<1]+len[p<<1|1];//否则用儿子更新
}
void change(int p,int l,int r,int L,int R,int v) {
if(L<=l&&r<=R) {
cnt[p]+=v;
updata(p,l,r);//每次覆盖都需要更新
return;
}
int mid=(l+r)>>1;
if(L<=mid)change(p<<1,l,mid,L,R,v);
if(R>mid)change(p<<1|1,mid+1,r,L,R,v);
updata(p,l,r);
}
}T;
int main() {
n=read();
for(int i=1;i<=n;i++) {
int x1=read(),y1=read(),x2=read(),y2=read();
tmp[(i<<1)-1]=y1,tmp[i<<1]=y2;
p[(i<<1)-1]=line(x1,y1,y2,1);
p[i<<1]=line(x2,y1,y2,-1);
}sort(p+1,p+2*n+1);sort(tmp+1,tmp+2*n+1);
cnt=unique(tmp+1,tmp+2*n+1)-tmp-1;
for(int i=1;i<=2*n;i++) {
p[i].st=lower_bound(tmp+1,tmp+cnt+1,p[i].st)-tmp;
p[i].ed=lower_bound(tmp+1,tmp+cnt+1,p[i].ed)-tmp;
}//排序和离散化
for(int i=1;i<=2*n;i++) {
ans+=1ll*T.len[1]*(p[i].x-p[i-1].x);//累加第i-1号区间的面积
T.change(1,1,cnt-1,p[i].st,p[i].ed-1,p[i].mark);//更新直线上被覆盖的长度
}printf("%lld\n",ans);
return 0;
}