首页 > 其他分享 >扫描线

扫描线

时间:2023-11-17 12:55:05浏览次数:28  
标签:cnt len ls 扫描线 include 节点

AcWing 247. 亚特兰蒂斯

题意:给定若干个矩形(长宽均平行于坐标轴),求它们的面积并(矩形的顶点坐标可以是实数)。

本题是扫描线算法的模板题。

扫描线,顾名思义,就是按照一条线扫过去,对于本题

扫描线 - OI Wiki (oi-wiki.org)

img

如上图,这就是扫描线的过程。

发现按照线扫描的话,每次我们只需要维护出非零的位置个数,然后乘上两条线之间的距离就可以了。

这个数据结构需要支持区间增/减1(减法保证在这之前有加法),还有根节点查询。

如果使用懒标记,麻烦无比,下面考虑不用懒标记的维护方法。

令 \(cnt\) 表示这个节点自己恰好被覆盖的次数(不考虑父节点、子节点的合并),\(sz\) 表示不考虑祖先的覆盖长度。

若当前节点有cnt,长度就是整个区间;否则从两个子节点相加;每次修改节点需要立即pushup。(注意需要判断叶子节点,否则会导致4n的位置访问8n的内存)。

关于扫描线 线段树不需要pushdown的理解:
因为他只是往下看的,还有一个cnt,只要祖先有cnt,他就无所谓是什么。
如果它的子节点更新了,若祖先没有了cnt,那它得到的子节点信息也一定是最新的。
注意cnt表示这个区间作为完整区间覆盖的次数(就是分裂出来覆盖的次数)。
注意这里维护出来的len是只考虑这个子树内的懒标记得出的(虽然可能是错的,反正只要祖先节点整体覆盖就行了,管他是什么)。
更新了一个位置,那么它的子节点因为不考虑父节点,肯定是对的,然后他自己也是对的,往上如果有cnt标记,无影响;否则从两个子节点+也是对的。(贡献会被累加)

然后因为下标不一定是整数,所以需要离散化,线段树维护的实际是 \([y_a,y_a+1]\) 的区间。

比如 \(1,1.2,1.3,1.9\),对应成三个数 \([1,1.2],[1.2,1.3],[1.3,1.9]\),建立线段树。

综上,可以 \(O(n\log n)\)。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<cstdlib>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l,i##end=r;i<i##end;++i)
#define Rs(i,l,r) for(int i=l,i##end=r;i>i##end;--i)
#define Le(i,l,r) for(int i=l,i##end=r;i<=i##end;++i)
#define Re(i,l,r) for(int i=l,i##end=r;i>=i##end;--i)
#define L(i,l) for(int i=0,i##end=l;i<i##end;++i)
#define E(i,l) for(int i=1,i##end=l;i<=i##end;++i)
#define W(t) while(t--)
#define Wh while

const int N=10010,M=2*N,K=4*M;
int n,len,cnt[K],l[K],r[K];
double x1[N],y1[N],x2[N],y2[N],ans,ls[M],sz[K];
struct seg{
	double x;
	int l,r,s;
	bool operator<(const seg &A)const{
		return x<A.x;
	}
}s[M];
void pushup(int p){
	if(cnt[p])sz[p]=ls[r[p]+1]-ls[l[p]];
	else if(l[p]==r[p])sz[p]=0;
	else sz[p]=sz[p<<1]+sz[p<<1|1];
}
void update(int p,int L,int R,int k){
	if(L<=l[p]&&r[p]<=R){
		cnt[p]+=k;
		pushup(p);
		return;
	}
	int mid=(l[p]+r[p])>>1;
	if(L<=mid)update(p<<1,L,R,k);
	if(R>mid)update(p<<1|1,L,R,k);
	pushup(p);
}
void build(int p,int L,int R){
    if(p>80080){
        cout<<"SB";
        exit(0);
    }
	l[p]=L,r[p]=R;
	if(L==R)return;
	int mid=(L+R)>>1;
	build(p<<1,L,mid),build(p<<1|1,mid+1,R);
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("1.in","r",stdin);
#endif
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t=0;
	Wh(cin>>n,n){
		ans=len=0;
		++t;
		E(i, n)cin>>x1[i]>>y1[i]>>x2[i]>>y2[i],ls[len++]=y1[i],ls[len++]=y2[i];
		sort(ls,ls+len);
		len=unique(ls,ls+len)-ls;
//		L(i, len)cout<<ls[i]<<' ';
//		cout<<'\n';
		build(1,0,len-2);
		int tot=0;
		E(i, n){
			int l=lower_bound(ls,ls+len,y1[i])-ls,r=lower_bound(ls,ls+len,y2[i])-ls;
			s[tot++]={x1[i],l,r,1};
			s[tot++]={x2[i],l,r,-1};
		}
		sort(s,s+tot);
		L(i, tot){
			if(i)ans+=(s[i].x-s[i-1].x)*sz[1];
//			cout<<s[i-1].x<<' '<<s[i].x<<' '<<sz[1]<<'\n';
//			cout<<s[i].l<<' '<<s[i].r-1<<'\n';
			update(1,s[i].l,s[i].r-1,s[i].s);
		}
		cout<<"Test case #"<<t<<'\n';
		cout<<fixed<<setprecision(2)<<"Total explored area: "<<ans<<"\n\n";
	}
	return 0;
}

标签:cnt,len,ls,扫描线,include,节点
From: https://www.cnblogs.com/wscqwq/p/17838501.html

相关文章

  • 【模板】扫描线
    【模板】扫描线题目描述求$n$个四边平行于坐标轴的矩形的面积并。输入格式第一行一个正整数$n$。接下来$n$行每行四个非负整数$x_1,y_1,x_2,y_2$,表示一个矩形的四个端点坐标为$(x_1,y_1),(x_1,y_2),(x_2,y_2),(x_2,y_1)$。输出格式一行一个正整数,表示$n$个......
  • 浅谈扫描线
    Preface本文部分题目摘自lxl的数据结构系列课件由于工程量巨大,难免会出现错字、漏字的情况,如果影响到了您的阅读体验,还请私信我,我会在第一时间修复。本文建议大家有一定的数据结构基础后再来阅读/heart。个人感觉扫描线不是难点,难点在于怎么抽象模型。首先需要明白一个东......
  • 扫描线
    假设有一条扫描线从一个图形的下方扫向上方(或者左方扫到右方),那么通过分析扫描线被图形截得的线段就能获得所要的结果。该过程可以用线段树进行加速。P5490【模板】扫描线扫描线可以处理两类问题一类是面积并一类是周长并#include<iostream>#include<algorithm>usingnames......
  • Auguryの扫描线分享
    Auguryの扫描线分享扫描线是啥有时候答案是不好计算的,但是答案可以拆分成多个段分别计算,且段与段之间可以快速转换,我们就可以用扫描线解决。或者说,一个二维问题,我们可以用扫描线变成一维。前置芝士线段树、值域线段树、树状数组(胡扬好闪,拜谢胡扬)离散化现在我们有一堆数,你......
  • 写个扫描线吧
    虽然扫描线很简单,但是我发现理解还是有些不清晰,于是决定写一篇。原问题,或者说是例题一个不好暴力东西,暴力模拟不带优化至少O(n^3)也是一个非常经典的线段树例题(因为懒得画图所以下面就直接用网上dalao的题解里面的图片了)首先,因为我们要在线段树上面记录下来每一个起始点和结束点,所......
  • 扫描线
    #include<bits/stdc++.h>#definemaxn200005#defineintlonglongusingnamespacestd;intx[maxn];//x[i]表示从左往右第i条竖边inttsum[maxn<<3],tlen[maxn<<3];//tsum表示当前节点被覆盖的次数//tlen是当前节点所对应的长方形的长voidpushup(intk,intl,intr)/......
  • 扫描线补充
    1.两条扫描线之间,不一定是一个矩形,可能是多个不相交的,高相同的矩形2.扫描线的板子没有pushdownvoidupd(intu,intl,intr){ if(cnt[u]){ len[u]=b[r+1]-b[l]; sum[u]=2; lh[u]=rh[u]=1; }else{ len[u]=len[lch]+len[rch]; sum[u]=sum[lch]+sum[rch]; lh[u]=lh[......
  • P5490 【模板】扫描线
    \(P5490\)【模板】扫描线一、题目描述求\(n\)个四边平行于坐标轴的矩形的面积并。输入格式第一行一个正整数\(n\)。接下来\(n\)行每行四个非负整数\(x_1,y_1,x_2,y_2\),表示一个矩形的四个端点坐标为\((x_1,y_1),(x_1,y_2),(x_2,y_2),(x_2,y_1)\)。输出格式......
  • 【单调队列】 单调队列的“扫描线”理解
    【单调队列】单调队列的“扫描线”理解  “如果一个选手比你小还比你强,你就可以退役了。”——单调队列的原理比你强,而且比你影响时间更长。某种意义上,数学思维是生活中的思考的延伸。  算法学习笔记(66):单调队列。引用Pecco的算法笔记。  在这里给出一种扫描线......
  • 扫描线学习笔记
    0.写在前面扫描线好闪,拜谢扫描线1.问题的引入在一个二维的坐标系上,给出多个矩形,求他们的面积并2.问题的分析假设我们有这么一张图你要求这三个矩形的面积并,可以考虑容斥原理,但这样会TLE但总之,他最终的结果是围成了一个多边形那你不妨考虑,重新分割这个最终的图形那......