Loading

扫描线

正交范围

在一个 \(B\) 维平面直角坐标系下,第 \(i\) 维限制在整数 \([l_i,r_i]\) 间,形成的点集,叫做 \(B\) 维正交范围。

例题 \(1\)

给定 \(\{a_n\}\)\(m\) 次查询 \(a\) 的一个区间内有多少个值只出现过一次。

对于一个位置 \(i\),设它的前驱、后继位置分别是 \(p,q\)。那么对于 \(l\)\((p,i]\)\(r\)\([i,r)\) 的询问,\(i\) 会对这些询问产生 \(1\) 的贡献。

于是变成了:\(n\) 次矩形加,然后是 \(m\) 次单点查询。直接扫描线就行了。

区间子区间问题

多次询问某个区间内有多少个子区间满足某个条件。

\(l\) 为横轴,\(r\) 为纵轴建立平面直角坐标系,那么一个区间 \([l,r]\) 对应着坐标系中一个点。

画出来长这样:

假如我们要询问 \([L,R]\) 中有多少满足条件的子区间,那么就是询问图中的蓝色区域:

假如能保证所有 \(l>r\) 的位置都不会产生贡献,那询问的实际上是一个 2-side 矩形:

例题 \(2\)

给定一个长为 \(m\) 的操作序列,有三种操作:

  • 区间赋值;
  • 交换两个位置的值;
  • 单点求值。

\(q\) 次询问,每次问初始将序列赋值成 \(0\),然后执行 \([l,r]\) 间的操作,得到的答案之和是多少。

扫右端点,每次在最后加一个操作是比较困难的。考虑倒着扫。

枚举左端点 \(l\),维护所有右端点的答案。对于每一个位置的询问开一个 vector,vector 内的元素表示这个询问的答案还是 \(0\)。当 \(l\) 左移一位时:

  • 若新的操作是区间赋值,那就相当于把区间内的所有 vector 里的询问都定成这个值,然后清空这些 vector。对一个询问定值,会影响到包含这个询问的区间的答案。而这些区间构成了一个后缀,所以线段树维护即可。
  • 交换:把两个 vector 交换一下;
  • 查询:push_back。

这个是有均摊性质的:vector 内一共只会有 \(\mathcal{O}(m)\) 个元素,每个元素被删的时候会有 \(\mathcal{O}(\log m)\) 的复杂度。所以总复杂度是 \(\mathcal{O}((q+m)\log m)\)

posted @ 2021-12-19 11:58  Alan_Zhao_2007  阅读(77)  评论(0编辑  收藏  举报