二维数点问题及其思想探究


二维数点问题及其思想探究

最近在各个地方接触的一些题目,思索了一下发现居然都是一个东西

\(\quad\)

起源是在HDU多校上遇到了这么一个题:

给一个字符串 \(s\) ,有多次询问,每次询问给出 \(x,y\),要求出 \(s\) 的前 \(x\) 个字符和后 \(y\) 个字符连接组成的模式串在 \(s\) 中出现了多少次。

当时KMPAC自动机SAM一通乱搞,想出了好几种建图方案,但最后都不知道怎么维护答案

赛后看题解说二维数点,学长拍脑门说会了,然而我还没看懂

\(\quad\)

然后是数据结构课上zngg掏出了一道老题:

HH的项链(看到名字有的人大概已经知道题意了

就是给一串 \(n\) 个珠子组成的项链,每个珠子有一个颜色,多次询问 \([l,r]\) 区间内出现了多少种颜色

这个题本身比较简单,把询问按右端排序后扫一遍,每次在树状数组中令右端位置权值+1,如果出现过就把其上一次出现的位置在树状数组中-1,这样维护了之后每次询问就可以直接在树状数组中查询 \([l,r]\)? 的权值和了

优化本质在于离线利用了查询区间的单调性

\(\quad\)

再来看二维数点问题:

n*m 的棋盘上有 k 个点,多次询问(a,b)到(c,d)这个子棋盘中有多少个点

n,m,k数据规模都是1e5

看了一圈博客,有人说二维数点本质是三维偏序问题,可以用CDQ分治做

于是我回忆了一下CDQ分治:

三维偏序问题要求数组中 \(i\(a_i < a_j\)\(b_i 的点对数

把数组按mid分成两段后,CDQ分治只关心横跨mid两端的点对,所以它将左右两端区间分别按 \(a\) 排序,用双指针,\(r\) 指针扫描右半边,同时将左半边所有 \(a_l 的点的 \(b\)? 权值加入树状数组

这样维护之后,右边每扫到一个点 \(a_r\) 时都可以直接在树状数组中查询小于 \(b_r\) 的数的个数了

仔细一想,发现跟上面那个项链题有异曲同工之妙:离线顺序处理 + 树状数组下标表示权值

\(\quad\)

想到了这一步的话,二维数点问题已经迎刃而解了:

首先是把 \((c-f)\)??????? (如图)的一组询问拆成 \((a-f)\)??????? 和 \((a-d)\)??????? ,两个询问减一减就好了,画个图更清楚??

然后把点和询问都按照 \(x\)? 排序,从小到大扫一遍,同时把所有横坐标小于当前 \(x\)? 的点\(y\)? 都加入树状数组

这里跟CDQ分治很像,维护的是另一维,因为 \(x\) 坐标已经被我们排序搞好了

然后对于每一个询问,都可以直接在树状数组中查询了,比如扫到cd时就查询树状数组中cd这段区间有多少点