首页 > 其他分享 >【学习笔记】扫描线

【学习笔记】扫描线

时间:2023-07-31 16:23:34浏览次数:42  
标签:int updata 线段 长方形 笔记 学习 扫描线 节点

扫描线是用来求解图形面积并的一个算法。

问题引入

给定 \(n\) 个长方形,求它们的面积并。下面以两个长方形为例:

对于这个问题,可以有容斥等做法,但是还有个更简单的方法——扫描线。

扫描线

扫描线,顾名思义,就是,拿一条“线”取扫(这里是从下往上扫,其实其它的扫的方式也是可以的):

如图所示,这几个图形被四条线分成了 \(3\) 个部分,我们可以对每一个部分的面积计算并累加起来。由于这些部分都是长方形,而这些长方形的宽很好计算,就是线扫过的距离(图中橙色箭头所标),而要求的就是长方形的长。

把每个矩阵的下面的边的值记作 \(1\),上面的边值记作 \(-1\),每次经过值为 \(1\) 条边就把对应边加进来,否则删去,那么现在长方形的长就是已加的边组成的边的长度(注意不是所有已加边的长度总和)。

暴力显然是不行的,考虑用数据结构来优化。

线段树优化扫描线

观察到,根据矩形的“横边”,可以把整个图形分为 \(3\) 个部分:

可以建一棵线段树来维护这三个部分。

具体的,线段树的每个节点都对应其的一部分,比如节点 \(2\) 对应的就是第一、二部分,节点 \(6\) 对应的就是第三部分。每个节点又维护了两个信息:第一是当前节点被覆盖的次数,第二是当前节点所对应的长方形的长。而长方形的长就是根节点所维护的第二个信息。

为什么要这么维护呢?第二个原因显然,而第一个如果对于每个 \(+1-1\) 的修改,都访问到叶子节点,那么复杂度就爆了,所以在把目标区间拆分成一个个小区间时,就修改区间对应的节点即可(其实有些类似懒标记)。

对应的,线段树要求能从子节点推到父节点,也就是 pushup 操作如下:

int X[N];//X[i] 表示从左往右第 i 条竖边
int tsum[N << 3], tlen[N << 3];//tsum 表示信息一,tlen 表示信息二
void pushup(int k, int l, int r) {//tsum 是由 +1-1 决定的,所以只更新 tlen
	if (tsum[k]) tlen[k] = X[r + 1] - X[l];//若整个区间都被访问过了则 tlen 为对应的区间长度
	else tlen[k] = tlen[k << 1] + tlen[k << 1 | 1];//否则为左右节点的长方形长之和
}

注意,在 pushup 中,包括之后的所有操作,节点 \(k\) 在线段树中维护的区间为 \([l,r]\),实际的区间为 \([X_l,X_{r+1}]\)(其实这个操作就是离散化),\(r\) 要加一是因为否则叶子节点维护的就是一个点了。

接下来是 updata。其实就是对每一条线段对应的区间进行更新,不过要注意边界的问题(其实可以这么想,区间对应的 \(l,r\) 是线段左端点的范围):

void updata(int k, int l, int r, int L, int R, int v) {
    //注意区分这里的 L, R 指的是实际的区间而不是 X 的下标
    //而 l, r 是指在 X 中的下标范围
	if (L <= X[l] && X[r + 1] <= R) {//在区间内就直接更新 tsum
		tsum[k] += v;
		return pushup(k, l, r);//注意这里也要更新 tlen 因为 tlen 也是受 tsum 影响的
	}
	int m = l + ((r - l) >> 1);
	if (L < X[m + 1]) updata(k << 1, l, m, L, R, v);
	//这里和一般的线段树不一样,比如要更新 [1,3],并不用更新 [3,4] 这个区间,所以不取等号
	//还有一点要时刻注意区间 [l,m] 对应 X 的范围是 [l,m+1]
	if (R > X[m + 1]) updata(k << 1 | 1, m + 1, r, L, R, v); 
	pushup(k, l, r);
}

具体的过程还是以上面的例子为例(其中蓝色表示当前更新的边,粉色表示当前长方形的宽,标在节点中的两个数表示 \(tsum\) 和 \(tlen\)):

然后是删边的两个操作:

PS:其实在实际过程中可以不用更新最后一条边因为更新了也不会被统计到了。

最后是完整的代码(洛谷 P5490 【模板】扫描线),还是要注意边界的问题:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5;//两条边所以开两倍
struct node {
	int l, r, h, v;
}Y[N];
int X[N];
bool cmp(node x, node y) {//从下到上排序
	return x.h < y.h;
}
int tsum[N << 3], tlen[N << 3]; //这里有个问题就是在 pushup 访问时可能会越界就多开一点
void pushup(int k, int l, int r) {
	if (tsum[k]) tlen[k] = X[r + 1] - X[l];
	else tlen[k] = tlen[k << 1] + tlen[k << 1 | 1];
}
void updata(int k, int l, int r, int L, int R, int v) {
	if (L <= X[l] && X[r + 1] <= R) {
		tsum[k] += v;
		pushup(k, l, r);
		return;
	}
	int m = l + ((r - l) >> 1);
	if (L < X[m + 1]) updata(k << 1, l, m, L, R, v);
	if (R > X[m + 1]) updata(k << 1 | 1, m + 1, r, L, R, v); 
	pushup(k, l, r);
}
signed main() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;//注意输入的是先纵坐标后横坐标
		X[2 * i - 1] = x1, X[2 * i] = x2;
		Y[2 * i - 1] = {x1, x2, y1, 1}, Y[2 * i] = {x1, x2, y2, -1};
	}
	sort(Y + 1, Y + 1 + 2 * n, cmp);
	sort(X + 1, X + 1 + 2 * n);
	int len = unique(X + 1, X + 1 + 2 * n) - X - 1, ans = 0;//去重
	for (int i = 1; i < 2 * n; i++) {
		updata(1, 1, len - 1, Y[i].l, Y[i].r, Y[i].v);//还是注意是 len-1 因为对应的是“左端点”
		ans += tlen[1] * (Y[i + 1].h - Y[i].h);//宽度是下一条边高度减当前边高度
	}
	cout << ans;
	return 0;
}

标签:int,updata,线段,长方形,笔记,学习,扫描线,节点
From: https://www.cnblogs.com/happyzero/p/17593744.html

相关文章

  • 【Python&目标识别】Labelimg标记深度学习(yolo)样本
    ​    人工智能、ai、深度学习已经火了很长一段时间了,但是还有很多小伙伴没有接触到这个行业,但大家应该多多少少听过,网上有些兼职就是拿电脑拉拉框、数据标注啥的,其实这就是在标记样本,供计算机去学习。所以今天跟大家分享下如何使用Labelimg去自己标记深度学习样本。......
  • 【机器学习】逻辑回归
    LogisticRegression分类问题本质是分类,要预测的变量是离散的值逻辑回归模型数学表达式\[z=\vecw\cdot\vecx+b\tag{1}\]\[f_{\vecw,b}(\vecx)=g(z)\tag{2}\]\[g(z)=\frac{1}{1+e^{-z}}\tag{3}\]=>\(f_{\vecw,b}(\vecx)=\frac{1}{1+e^{-(\ve......
  • 【机器学习】softmax回归
    SoftmaxRegression(多标签分类)将多输入的分类值转化为[0,1]的概率分布,进而进行逻辑回归算法softmax能将差距大的数值距离拉得更大,但是数值可能会溢出SoftmaxFunction数学表达式\[a_j=\frac{e^{z_j}}{\sum_{k=1}^{N}{e^{z_k}}}\]代码defmy_softmax(z):ez=n......
  • 【机器学习】协同过滤
    CollaborativeFilteringRecommenderSystems解决相似度问题概念准确率=\(accuracy=\frac{预测正确的样本}{总样本}\)精确率=\(precision=\frac{预测成功的正类}{预测的正类}\)【不能误检】召回率=\(recall=\frac{预测成功的正类}{总正类}\)【不能漏报】相......
  • 【机器学习】正则化
    RegularizedCostfunction forregularizedlinearregression数学表达式\[J(\mathbf{w},b)=\frac{1}{2m}\sum\limits_{i=0}^{m-1}(f_{\mathbf{w},b}(\mathbf{x}^{(i)})-y^{(i)})^2+\frac{\lambda}{2m}\sum_{j=0}^{n-1}w_j^2\]\[f_{\mathbf{w},b}(......
  • 【机器学习】决策树
    DecisionTree熵-entropy数学表达式\[H(p_1)=-p_1\text{log}_2(p_1)-(1-p_1)\text{log}_2(1-p_1)\]代码#UNQ_C1#GRADEDFUNCTION:compute_entropydefcompute_entropy(y):"""ComputestheentropyforArgs:y(n......
  • 【机器学习】K-Means
    K-Means找最接近的质心公式\[c^{(i)}:=j\quad\mathrm{that\;minimizes}\quad||x^{(i)}-\mu_j||^2\]其中,范式\(||X||\),其计算公式为\[||X||=\sqrt{x_1^2+x_2^2+\cdots+x_n^2}\]代码#UNQ_C1#GRADEDFUNCTION:find_closest_centroidsdeffind_closest......
  • sipeed公司SLogic Combo8体验使用笔记
    sipeed公司SLogicCombo8使用体验笔记一、简介SLogiccombo8是一款基于SipeedM0sDock进行二次开发而成的逻辑分析仪,同时还兼有CKLinkDebugger、DAP-LinkDebugger、USB2UART功能,通过按键可以任意切换功能。二、功能参数2.1.SLogic功能参数2.2.CKLink功能参数2.3.......
  • DRF之APIView全笔记
    一.APIView基本视图,所有的都用这个来作viewsetmixin主要管as_view{}里的调配让视图不再需要两个类二.通用视图GenericAPIView(rest_framework.viewsets)GenericAPIView一共五个功能,数据库获取、分页、序列化、getobject\还有frilter_queryset__东西挺多的主要管self.get_object......
  • 爬虫学习(一)
    爬虫学习(一)简单爬虫我们需要学习urllib库,在这个库中存在着许多辅助我们进行爬虫的工具,该包中有着模块:request:最基本的HTTP请求模块,可以用来模拟发送请求。error:异常处理抹开,如果出现请求错误,可以捕捉异常,然后进行充实或其他操作。parse:工具模块,提供了许多URL处理方法,如拆分,......