范围修改查询问题
http://www.nfls.com.cn:10611/up/paper/国家集训队2024论文集.pdf P63
引入
这部分作者定义了半群和幺半群来描述一般的线段树可以做到的结构
-
半群:结合律
-
幺半群:结合律、有幺元
-
交换半群:结合律、交换律
形式化问题:(以下是通俗易懂的版本)
- 给定交换半群 \(D\)(“数据”)和半群 \(M\)(“标记”),数据经过两次标记的操作可以转化为数据经过(两次标记复合得到的标记)的操作,标记对数据的加法有分配律。要求支持范围打标记范围求和
以下称“范围打标记”为 apply
,范围求和为 accumulate
最优离线范围修改查询算法
算法
问题:给定点集的划分方式 \(f_i\)(\(f_i\) 是点集集合),\(n\) 次操作,每次操作为:
对 \(p\in f_i\) 的所有点 \(p\)
apply
对 \(p\in f_i\) 的所有点 \(p\)
accumulate
一个 \(f_i\) 的集合 \(\{f_1,f_2,\cdots\}\) 会将点集划分为若干等价类
我们用森林结构刻画等价类。每个树对应等价类。两个等价类的合并即为:新建一个节点,然后将这两棵树的根节点设为这个节点的儿子
考虑分治。初始时我们有点集经过询问 \([1,q]\) 的划分方式 \(P_{1,q}\)。当我们知道 \(P_{l,r}\) 时,我们可以:
-
合并 \([mid+1,r]\)
-
递归 \(P_{1,mid}\)
-
撤销合并,并合并 \([l,mid]\)
-
递归 \(P_{mid+1,r}\)
到叶子节点的时候,我们想要的那个划分一定是一棵树,在这棵树上 apply
或 accumulate
即可
在合适的时刻需要 push_up
或者 push_down
复杂度
设 \(P(n)\) 表示 \(n\) 个划分可以把点集划分为多少个区域,则 \(T(n)=T({n\over 2})+P(n)\)
设有 \(n\) 个点 \(m\) 次询问,执行一次算法的复杂度为 \(O(n)+T(m)\)
当划分为线段时,\(P(n)=O(n)\),则复杂度为 \(O(n+m\log m)\)
当划分为二维曲线,如圆、椭圆、凸多边形、双曲线,复杂度为 \(O(n+m^2)\)。考虑每 \(\sqrt\) 个点运用上述算法即可平衡到 \(O(n+m\sqrt n)\)
\(d\) 维空间范围为 \(O(n+mn^{1-{1\over d}})\)
例:半平面修改查询
使用以上算法。考虑如何找到划分
首先初始时的划分可以通过扫描线找到:对于 \(O(B)\) 条线,两两求出交点,然后按照一维扫描线,维护线的顺序
扫描线的过程中可能会出现线顺序交换的情况,维护即可
每一条线上开一个链表挂上这条线的交点
合并的时候,找到这条线合并即可,需要更新链表
单点查询
单点查询的时候,我们可以只维护划分方式,只要查询的时候可以快速定位到区域即可
例:P7881
先考虑不强制在线的做法
每 \(B\) 个操作分块
使用二维数组刻画划分方式,我们有 \(O({n\over B})\) 个二维数组
每次查询的时候在整块的二维数组中二分得到对应答案,散块直接暴力
当强制在线的时候,我们把散块暴力改为二进制分组即可。显然这个结构支持合并,我们只需要重新排序即可
半平面范围查询
半平面范围上的划分有若干形式
注:以下为静态查询问题
数据随机算法
KDT
直接 KDT 的复杂度是错的,但是在数据随机的时候可以使用
平面分块
每 \(\sqrt n\times \sqrt n\) 个区域分成一块,每个块期望 \(O(1)\) 个点
枚举每一个横行,整块是每个横行的前缀或后缀,散块暴力统计
是效率最高的算法
例题
- uoj 533
式子转化后发现是半平面数点
- P8261
半平面颜色平方和
考虑 KDT
首先,把询问和修改反演,通过式子变形转化为这样的问题:
给定若干半平面,每个半平面有颜色,对于一个询问的点求包含它的半平面的颜色平方和
考虑这个问题的序列版本怎么做:对于每个颜色,设出现次数为 \(cnt_c\),把区间离散化,则会分为 \(O(cnt_c)\) 段,每段单独修改即可
如果在线段树上,则可以给每个节点一个 \(tot\),每次对应节点的子树打 \(tot\leftarrow tot+1\) 的标记,把该颜色的这种操作做完后,枚举所有访问过的虚树的叶子节点,给这些节点的子树进行修改
在 KDT 上做以上过程即可
- P9996
旋转扫描线
定义一个半平面的“标准型”为:先向下移动半平面直到碰到点,然后旋转半平面直到碰到点
显然 \(O(B)\) 个点只有 \(O(B^2)\) 种半平面的标准型
于是我们证明了 \(O(B)\) 个点只有 \(O(B^2)\) 种半平面划分方式
随机一个点作为原点,然后枚举斜率进行旋转,此时我们在讨论一条线绕着一个点旋转的动态过程
考虑每个点到这条线的投影,这些投影的顺序变化只会发生 \(O(B^2)\) 次
枚举两两连线即可找到变化的时机,共 \(O(B^2)\) 个,把变化挂在对应时机上
在对应的时机查询即可。数据结构只需要支持交换相邻元素,查询前缀或后缀。可以 \(O(1)\) 维护
半平面莫队
P8529
维护点集,使用 \(O(n\sqrt n)\) 次操作,每次加点、删点,使得每一个半平面区域都至少出现一次
随机选取 \(O(B)\) 个点,旋转扫描线,在斜率时刻暴力移动截距,这部分 \(O(n\sqrt n)\)
我们把一个半平面挂在截距最小的那个原点上,则每个半平面期望移动 \(O(\sqrt n)\) 次