扫描线进阶

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扫描线进阶相关的知识,希望对你有一定的参考价值。

扫描线进阶

1.矩形面积并

P5490 【模板】扫描线

x轴平行线段按 y y y排序,离散化+点映射为线段后,从下往上线段树操作计算答案即可。

底边权为1,高边权为-1。

每次计算的面积是当前线段的长度与沿上走的高度矩形面积。

最后一条边不用算。

用一个tag标记该区间是否覆盖,如果被覆盖长度即为右端点-左端点。

否则用儿子更新长度。

code

ll X[N<<1];
struct node{
	ll l,r,s,len;//l,r,s,len 结点区间表示线段[X[l],X[r+1]],s表示线段的个数,和线段长度. 
}a[N<<2]; 
struct Line{	//储存所有矩形横边,从下往上扫描. 
	ll l,r,h,c;	//l,r,h,c分别为横线左端点,右端点,距离x轴高度,下边权值c=1,上边权值c=-1> 
	bool operator<(const Line&li)const{
		return h<li.h;
	}
}line[N<<1];
void build(int x,int l,int r){	//建树 
	a[x].l=l,a[x].r=r;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(lx,l,mid);
	build(rx,mid+1,r);
}
void pushup(int x){	//更新. 
	int l=a[x].l,r=a[x].r;
	if(a[x].s) a[x].len=X[r+1]-X[l];	//如果当前线段已经被记数了,直接取长度 
	else a[x].len=a[lx].len+a[rx].len;//否则更新. 
}
void update(int x,int L,int R,int val){	//更新线段记数情况. 
	int l=a[x].l,r=a[x].r;
	if(X[r+1]<=L||X[l]>=R) return; //不在范围内,这里取等于 防止单点重合的情况.
	if(X[l]>=L&&X[r+1]<=R){
		a[x].s+=val; 
		pushup(x); //这里相当于直接更新长度,不用下方标记.
		return;
	}
	update(lx,L,R,val);
	update(rx,L,R,val);
	pushup(x); //合并信息.
}
int main(){
	int n,cnt=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		X[(i<<1)-1]=x1,X[(i<<1)]=x2;
		line[(i<<1)-1]={x1,x2,y1,1};
		line[(i<<1)]={x1,x2,y2,-1};
	}
	n<<=1;
	sort(line+1,line+n+1);
	sort(X+1,X+n+1);
	cnt=unique(X+1,X+n+1)-X-1;	//离散化所有端点的横坐标X.从小到大排序. 
	build(1,1,cnt-1);
	ll ans=0;
	for(int i=1;i<n;i++){	//<n 不用计算最后一条横边. 
		update(1,line[i].l,line[i].r,line[i].c);
		ans+=a[1].len*(line[i+1].h-line[i].h); 
	}
	printf("%lld\\n",ans);
	return 0;
}

2.矩形周长并

分成两部分计算,横边长度和+竖边长度和。

横边长就是 a [ 1 ] . l e n a[1].len a[1].len

横边长度就等于abs(上一次的横边长-这一次的横边长)

竖边长度和需要维护一个竖边的条数,每次贡献就是 2 × c n t × l e n 2\\times cnt \\times len 2×cnt×len

每个结点维护一个左、右端点tag(bool),和当前区间对应的 c n t cnt cnt

如果 l e n len len t a g tag tag标记为0,直接更新 l e n len len,然后左、右端点标记都赋值为真,然后 c n t = 1 cnt=1 cnt=1

否则,用儿子来更新 l e n , c n t len,cnt len,cnt

若当前区间的左儿子的左端点和右儿子的右端点均存在。

c n t − 1 cnt-1 cnt1,因为内部的两条线段会被减掉,不会计算贡献。因为这两个矩形被合并。

ll X[N<<1];
struct node{
	ll l,r,s,len,c;//l,r,s,len 结点区间表示线段[X[l],X[r+1]],s表示线段的个数,和线段长度. 
	bool lc,rc;
}a[N<<2]; 
struct Line{	//储存所有矩形横边,从下往上扫描. 
	ll l,r,h,c;	//l,r,h,c分别为横线左端点,右端点,距离x轴高度,下边权值c=1,上边权值c=-1> 
	bool operator<(const Line&li)const{
		if(h==li.h) return c>li.c;
		return h<li.h;
	}
}line[N<<1];
void build(int x,int l,int r){	//建树 
	a[x].l=l,a[x].r=r,a[x].lc=a[x].rc=a[x].len=a[x].c=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(lx,l,mid);
	build(rx,mid+1,r);
}
void pushup(int x){	//更新. 
	int l=a[x].l,r=a[x].r;
	if(a[x].s) {
		a[x].len=X[r+1]-X[l];
		a[x].lc=a[x].rc=a[x].c=1;
	}
	else {
		a[x].len=a[lx].len+a[rx].len;
		a[x].lc=a[lx].lc,a[x].rc=a[rx].rc;
		a[x].c=a[lx].c+a[rx].c;
		if(a[lx].rc&&a[rx].lc) a[x].c--;
	}
}
void update(int x,int L,int R,int val){	//更新线段记数情况. 
	int l=a[x].l,r=a[x].r;
	if(X[r+1]<=L||X[l]>=R) return; 
	if(X[l]>=L&&X[r+1]<=R){
		a[x].s+=val;
		pushup(x);                                                    
		return;
	}
	update(lx,L,R,val);
	update(rx,L,R,val);
	pushup(x);
}
int main(){
	int n,cnt=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		X[(i<<1)-1]=x1,X[(i<<1)]=x2;
		line[(i<<1)-1]={x1,x2,y1,1};
		line[(i<<1)]={x1,x2,y2,-1};
	}
	n<<=1;
	sort(line+1,line+n+1);
	sort(X+1,X+n+1);
	cnt=unique(X+1,X+n+1)-X-1;	//离散化所有端点的横坐标X.从小到大排序. 
	build(1,1,cnt-1);
	ll ans=0,pre=0;
	for(int i=1;i<n;i++){	
		update(1,line[i].l,line[i].r,line[i].c);
		ll x=a[1].len-pre;
		if(x<0) x=-x;
		ans+=x;
		pre=a[1].len;
		ans+=2*a[1].c*(line[i+1].h-line[i].h);
	}
	ans+=line[n].r-line[n].l;
	printf("%lld\\n",ans);
	return 0;
}

3.自适应辛普森积分

  • 解决定积分。
  • 解决到某一值就收敛的不定积分。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygDsInKY-1627029163662)(C:\\Users\\HeHao\\AppData\\Roaming\\Typora\\typora-user-images\\image-20210716184659237.png)]

即: ∫ l r f ( x ) d x = ( r − l ) ( f ( l ) + f ( r ) + 4 f ( m ) ) 6 , m = l + r 2 \\large \\int_l^r f(x)dx=\\dfrac{(r-l)(f(l)+f(r)+4f(m))}{6},m=\\dfrac{l+r}{2} lrf(x)dx=6(rl)(f(l)+f(r)+4f(m)),m=2l+r

模板代码

il db f(db x){
	// set function 

}
il db simpson(db l,db r){
	db m=(l+r)/2;
	return (f(l)+f(r)+4*f(m))*(r-l)/6;
}
il db asr(db l,db r,db eps,db ans){
	db m=(l+r)/2;
	db ls=simpson(l,m),rs=simpson(m,r);
	if(fabs(ls+rs-ans)<=eps*15) return ls+rs+(ls+rs-ans)/15;
	return asr(l,m,eps/2,ls)+asr(m,r,eps/2,rs);
}

P4525 【模板】自适应辛普森法1

纯板子 s i m p s o n simpson simpson定积分。

#define il inline
#define db double
db a,b,c,d,l,r;
il db f(db x){
	return (c*x+d)/(a*x+b《算法竞赛进阶指南》0x43线段树 扫描线算法 POJ2482

我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段

我的C语言学习进阶之旅解决 Visual Studio 2019 报错:错误 C4996 ‘fscanf‘: This function or variable may be unsafe.(代码片段

我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段

Android zxing Journeyapps 条码扫描器内部片段

我的Android进阶之旅关于Android平台获取文件的mime类型:为啥不传小写后缀名就获取不到mimeType?为啥android 4.4系统获取不到webp格式的mimeType呢?(代码片段