扫描线详解

扫描线详解

写在前面

阅读本文的一些前置知识

1. 线段树

2. 离散化

首先你要保证你会上面两种算法,下面我默认你们在会这两种算法的基础上讲解。

线段树可以查看我的洛谷博客,正在更新。

而离散化下面我会再提一次,所以其实你不会离散化也没关系。

一些话

笔者写下这篇文章,并不是盈利性的目的,而是为了更多人能了解扫描线这一种算法。

尽管我写的可能不如别人好,但是我一定尽力写好了这一篇文章。

纵然惊涛骇浪,仍要砥砺前行

前置知识补充

线段树

我并不打算展开这一部分的知识,具体内容可以查看我的博客。

离散化

笔者并没有专门写一篇关于离散化的博客,因此我在这详细讲解这一部分的内容。

什么是离散化?

离散化就是一个小数据映射到大数据的过程。

需要离散化一般有这样的数据提示信息:\(n≤10^5,1≤x_i≤10^18\),而你要做的是维护\(1-n\)这样一个序列的信息。

很明显如果以 \(x\) 作为下标进行储存是不可以的,当然 \(STL\) 为你提供了一种映射方式就是:\(map\)

但是很显然 \(map\) 常数很大,并且不是我们讨论的重点。

假设没有 \(map\),那么上面的问题将要怎么解决?

于是离散化横空出世。

为什么进行离散化?

上面已经说了,\(10^18\)的数组是开不了的,但是我们注意到这里的 \(n\) 非常的小(别想起状压好嘛,我在讲线段树!)

所以我们需要在 \(1-n\) 这个域上进行映射,那么原来每一个 \(x_i\) 就被投影到了 \(1-n\) 上,再利用 \(1-n\) 去维护即可。

这样我们就达到了维护的目的,还可以开的下数组,是不是很美妙?

如何进行离散化?

我们只需要一个方法建立起这个映射就可以了。

很显然不能乱建

然后我们考虑每一个数特有的属性。假设数字是不重复的,那么这个属性就是数字的大小!

所以我们可以根据大小建立映射。

首先,我们把要映射的数据投入一个数组,然后进行排序(至于排序的原因等会讲)。

如果数据可重但是你不需要维护数据信息而只是为了映射(映射当然可以多个到一个啦),那么去个重。

(当然如果所有的 \(x_i\) 都不相同也就不需要了)

\(STL\) 中为我们内置了一个去重的函数: \(unique\)。这个函数的使用要求就是数组必须是有序的。

所以我们需要排序。并且注意,这个去重函数是有返回值的,它返回不重复的元素后一位指针。

如果你想知道去重后还有几个数,只需要这么做:

int m = unique(Hash + 1, Hash + n + 1) - (Hash + 1);

因为 \(Hash\) 数组下标我从 \(1\) 开始,所以最后是减去\(Hahs + 1\),记住即可。

还有一点要注意:去重后的数组原来的数并没有消失,而是移到了数组的后端!

接下来你只需要每个数的大小,你可以通过 \(STL\) 中的 \(lower\_bound\) 实现。(返回的大于等于 \(x\) 的第一个数的指针)

然后你就知道了每个数的大小,所以就完成了离散化。

上面已经解决了不维护的数据或者是不重复的数据。

那么如果数据要维护并且会重复呢?

解决方案很简单,你只需要在一个数映射到的值域已经有值了后,在数组最末端插入这个数即可。

最后你仍然需要一个去重或者排序(因为剩下的数组中间会有空着的 \(0\))。

排序要注意把原来序列打乱,或者不使用 \(sort\),而是 \(stable\_sort\)

具体的代码实现就是这样的:(如果上面没看懂看看代码吧)

long long Hash[N], a[N];

int n;
scanf("%d", &n); 
for(int i = 1; i <= n; i++) {
    long long x;
    scanf("%lld", &x);
    Hash[i] = a[i] = x;
}

sort(Hash + 1, Hash + n + 1);
int m = unique(Hash + 1, Hash + n + 1) - (Hash + 1);
for(int i = 1; i <= n; i++) {
    a[i] = lower_bound(Hash + 1, Hash + m + 1, a[i]) - Hash;
    printf("%d ", a[i]);
}

上面的代码可以处理不重复数据或者不需要维护信息只需要知道值域的数据。

其实离散化之和并不需要你维护什么信息。下面这份代码就是可重数据映射。

(其实离散化真的不需要维护什么序列的,序列的维护一般使用线段树)

long long Hash[N], a[N];
bool v[N];
int n;
scanf("%d", &n); 
for(int i = 1; i <= n; i++) {
    long long x;
    scanf("%lld", &x);
    Hash[i] = a[i] = x;
}

sort(Hash + 1, Hash + n + 1);
int m = unique(Hash + 1, Hash + n + 1) - (Hash + 1), p = n;
for(int i = 1; i <= n; i++) {
    int x = lower_bound(Hash + 1, Hash + m + 1, a[i]) - Hash;
    if(v[x]) a[i] = ++p;
    else a[i] = x, v[x] = true;
    printf("%d ", a[i]);
}

上面那份代码结合线段树你可以完成下面这题:(其实很水的)

(还在出,出完放链接)

上面是离散化的大概内容。

扫描线

猜你喜欢

转载自www.cnblogs.com/Ning-H/p/11583942.html