首页 > 其他分享 >刍议线段树 3 (扫描线)

刍议线段树 3 (扫描线)

时间:2024-08-18 17:37:51浏览次数:12  
标签:线段 刍议 节点 扫描线 y1 y2 ll

扫描线

扫描线是一种另外的思想,只是其中会运用到线段树以求优化。所以不要将扫描线简单的并为线段树的一个小拓展。

例题:

https://www.luogu.com.cn/problem/P5490

大意:求 \(n\) 个四边平行于坐标轴的矩形的面积并。

思路:纵向分割图形

我们考虑把这些纵向矩形分割。
那么,总面积就为分割出的每一段矩形的面积和。

如上图,每扫描到一段,该段面积就是直线上覆盖的长度乘该段的宽度。

同时,我们用一个四元组 \((x,y1,y2,k)\) 表示每条纵向分割线。其中,当 \(k=1\) 时表示此边是矩形的左边的边,当 \(k=-1\) 时表示此边是矩形右边的边。

线段树则用来维护扫描线上被覆盖的长度(即纵向的长度),每次修改后,更新被覆盖长度。

更新代码如下:

void pushup(int x)
{
	if(t[x].cnt) t[x].len = disx[t[x].r + 1] - disx[t[x].l]; //cnt>0,面积就是直线上覆盖的长度乘该段的宽度。
	else t[x].len = t[x * 2].len + t[x * 2 + 1].len;//cnt=0,面积为其子节点的长度和。
}

这里拉一段李煜东的内容:

在本题我们只关心整个扫描线(线段树根节点)上被覆盖的长度。四元组又成对出现,所以线段树区间修改也是重复出现,这样就没必要下传延时标记,而采用更加简单的做法:在线段树每个节点上另加维护该节点代表的区间被矩形覆盖的长度 \(len\),该节点自身被覆盖的次数 \(cnt\)。对于一个四元组 \((x,y1,y2,k)\),在 \([val(y1),val(y2)-1]\)(此处的 \(val\) 指的是离散化后 \(y\) 的原始值) 上执行区间修改。该区间被线段树划分成 \(log N\) 个节点,把这些节点的
\(cnt\) 都加 \(k\) 。

代码

讲累了,贴代码了

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
ll n;
ll x1,y1,x2,y2;
#define FOR(i,_l,_r) for(ll i=_l;i<=_r;i++)
#define ls p<<1
#define rs p<<1|1
const int N=1e6+5;
int X[N<<1];
ll ans;
struct seg_line
{
	ll l,r;
	ll h;
	ll mark;
}line[N<<1];
bool cmp(seg_line a,seg_line b)
{
	return a.h<b.h;
}
struct srg_tree
{
	ll l,r;
	ll sum;
	ll len;
}tr[N<<2];
void up(int p)
{
	if(tr[p].sum)
		tr[p].len=X[tr[p].r+1]-X[tr[p].l];
	else tr[p].len=tr[ls].len+tr[rs].len;	
}
void build(int p,int l,int r)
{
	tr[p].l=l;tr[p].r=r;
	tr[p].len=tr[p].sum=0;
	if(l==r) return;
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	return;
}
void work(int p,ll L,ll R,int c)
{
	ll l=tr[p].l;ll r=tr[p].r;
	if(X[r+1]<=L||X[l]>=R) return;
	if(L<=X[l]&&X[r+1]<=R)
	{
		tr[p].sum+=c;
		up(p);
		return;
	}
	work(ls,L,R,c);
	work(rs,L,R,c);
	up(p);
}
int main()
{
	cin>>n;
	FOR(i,1,n)
	{
		cin>>x1>>y1>>x2>>y2;
		X[2*i-1]=x1;X[2*i]=x2;
		line[2*i-1]=(seg_line){x1,x2,y1,1};
		line[2*i]=(seg_line){x1,x2,y2,-1};
	}
	n<<=1;
	sort(line+1,line+n+1,cmp);
	sort(X+1,X+n+1);
	ll tot=unique(X+1,X+n+1)-X-1;
	build(1,1,tot-1);
	FOR(i,1,n-1)
	{
		work(1,line[i].l,line[i].r,line[i].mark);
		ans+=tr[1].len*(line[i+1].h-line[i].h);
	}
	cout<<ans;
	return 0;
}

本来以为这篇要烂尾了,结果还是糊弄完了,以后本人要先转战 \(DP\) ,数据结构暂时放下了。

完结撒花。

标签:线段,刍议,节点,扫描线,y1,y2,ll
From: https://www.cnblogs.com/yingxilin/p/18365846

相关文章

  • 线段树模板,洛谷原题P3373
    线段树区间乘、加,范围求和,QWQ原题#include<bits/stdc++.h>#definePIIpair<int,int>#defineintlonglong#defineDBdoublenamespaceFastIO{ inlineintread(intMOD,int&ret){ charch=getchar();intngtv=1; if(MOD==0){while(ch<&#......
  • D46 2-SAT+线段树优化+二分 [ARC069F] Flags
    视频链接: [ARC069F]Flags-洛谷|计算机科学教育新生态(luogu.com.cn)//D462-SAT+线段树优化+二分O(nlognlogv)#include<iostream>#include<cstring>#include<algorithm>usingnamespacestd;#definemid((l+r)>>1)#definels(u<<1)#definer......
  • leetcode线段树(2940. 找到 Alice 和 Bob 可以相遇的建筑)
    前言经过前期的基础训练以及部分实战练习,粗略掌握了各种题型的解题思路。现阶段开始专项练习。描述给你一个下标从 0 开始的正整数数组 heights ,其中 heights[i] 表示第 i 栋建筑的高度。如果一个人在建筑 i ,且存在 i<j 的建筑 j 满足 heights[i]<heig......
  • 算法笔记——可持久化线段树
    维护历史值当要修改一个节点时,把跟他有关的线段树中所有节点舍弃,并建立新节点连接.代码如下:#include<bits/stdc++.h>usingnamespacestd;constintN=1e6+5;intn,m,a[N],root[N],top;structnode{ intl,r,val;}t[N*40];intclone(intx)//新建节点{ top++; t......
  • 有关线段树的一些细节理解
    写在前面本菜鸡线段树学了好多遍但是每次写还是得很长时间有时有一个细节还要琢磨半天所以为了今后避免以上事情发生本菜鸡决定将这么长时间以来对线段树的认识汇总好日后清算模板#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e5;i......
  • zkw线段树
    事情的起因是我某天吃晚饭时打算找个电子榨菜,然后b站搜索线段树,看到了一个名叫zkw线段树(即非递归线段树),由于不是面向Oier的,所以饭后我又找了几个博客看,现在写下心得记录(其实只是不想在书签留3个位置给线段树)为什么要学习非递归线段树,这个问题大部分博客解释为普通线段树......
  • 『线段树合并』Day7
    颓了一天了。md虽然还没有过线段树合并板题,但早就用过了。注意,单次合并复杂度是\(O(n\logn)\)的,但是一直合并,保证最终共有\(n\)个元素的话,总时间复杂度也是\(o(n\logn)\)的。不要理解成单次\(\logn\)。一个人千万不能忘记自己的初心,有时候需要静下心来想一想自己到底......
  • 线段树杂谈
    动态开点线段树引入普通的线段树写法有一个显然的缺点——空间。堆式存贮使得我们开线段树时需要用$4n$的空间。冗余空间高达$2n$。而且,在大多数情况下线段树中并不是每个节点都会被用到,这时我们就可以使用动态开点线段树,不仅所用的空间小,实现起来的代码也比普通线段树......
  • 线段树+懒标记 (区间修改,区间查询)
    原作者:董晓P3372【模板】线段树1//结构体版#include<cstring>#include<iostream>#include<algorithm>usingnamespacestd;#defineN100005#defineLLlonglong#definelcu<<1#definercu<<1|1LLw[N];structTree{//线段树LLl,r,......
  • 李超线段树
    用途:用于二维坐标系维护多条线段。算法:本质上是采用标记永久化,对每个线段树节点维护一个标记表示该区间存在这一条线段,查询时从上到下经过节点的标记即为该横坐标上可能经过的线段。下面需在标记(线段)间的比较上作考虑:建议画图理解此时对于一个区间\([l,r]\),找出中点\(mid......