二维数点问题及其思想探究
二维数点问题及其思想探究
最近在各个地方接触的一些题目,思索了一下发现居然都是一个东西
\(\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<j\) 且 \(a_i < a_j\) 且 \(b_i<b_j\) 的点对数
把数组按mid分成两段后,CDQ分治只关心横跨mid两端的点对,所以它将左右两端区间分别按 \(a\) 排序,用双指针,\(r\) 指针扫描右半边,同时将左半边所有 \(a_l<a_r\) 的点的 \(b\) 权值加入树状数组
这样维护之后,右边每扫到一个点 \(a_r\) 时都可以直接在树状数组中查询小于 \(b_r\) 的数的个数了
仔细一想,发现跟上面那个项链题有异曲同工之妙:离线顺序处理 + 树状数组下标表示权值
\(\quad\)
想到了这一步的话,二维数点问题已经迎刃而解了:
首先是把 \((c-f)\) (如图)的一组询问拆成 \((a-f)\) 和 \((a-d)\) ,两个询问减一减就好了,画个图更清楚
然后把点和询问都按照 \(x\) 排序,从小到大扫一遍,同时把所有横坐标小于当前 \(x\) 的点的 \(y\) 都加入树状数组
这里跟CDQ分治很像,维护的是另一维,因为 \(x\) 坐标已经被我们排序搞好了
然后对于每一个询问,都可以直接在树状数组中查询了,比如扫到cd时就查询树状数组中cd这段区间有多少点