数据结构基础

线段树

扫描线

P8868 [NOIP2022] 比赛

U403634 糊纸

题目

CF983D Arkady and Rectangles

ふたつのアンテナ (Two Antennas)

可以直接对于询问进行扫描线,扫 \(j\),维护 \(i\) 答案。也就是线段树上下标维护的都是 \(a_i\) 的信息,每次扫到 \(j\) 的时候都用 \(a_j\) 往线段树里里面更新,类比普通线段树的区间加。

\(i+a_i\) 标记 \(i\) 可用,在 \(i+b_i+1\) 标记为不可用。只需要在扫描线扫到对应位置的时候插入删除即可。删除的时候直接将 \(Mx\)\(Mn\) 设为 \(0\)\(\infty\) 即可。

然后对于每个 \(j\)\([j-r_j,j-l_j]\)\(i\) 为可能集合。于是我们用 \(a_j\) 区间更新即可,线段树上维护 \(a_j-a_i\) 的极值。

扫描线很平凡,但是维护方式很新颖的技巧。

时间复杂度 \(O((n+m)\log n)\)

兔队线段树

P4198 楼房重建

记斜率为 \(k_i\),对于每个区间维护区间 \(k_i\) 最大值,还有考虑整个区间 \([l,r]\) 之后,\([mid+1,r]\) 的答案。

CF671E Organizing a Race

树状数组

\(c_i\) 维护的是 \((i-\operatorname{lowbit}(i),i]\) 的和。
后缀 BIT 就是把查询和修改操作跳 \(\operatorname{lowbit}\) 反过来,查询的时候就是 \((l)-(r+1)\)

P3586 [POI2015] LOG

简单题,不过有一个地方不太好办。对于每次询问很显然的转化就是求是否 \(\sum\min(a_i,s)\ge s\times c\),把 \(a_i\) 拆成 \(a_i\)\(1\) 相加,那么对于 \(s\),只要取前 \(s\)\(1\) 即可。我们可以用动态开点线段树来维护,对于一个 \(a_i\) 就把线段树上 \([1,a_i]\) 集体加 \(1\),查询就是对于区间 \([1,s]\) 求和。可是这显然通过不了 \(10^6\) 的数据。

考虑使用小常数数据结构树状数组,可以树状数组可以维护的值域有限。我们只能维护相对大小关系,于是我们统计出 \(a_i\ge s\) 的个数,然后用 \(c\) 减去这些,再对于 \(a_s \le s\) 求和,这样子就成功将维护信息转化成了有相对大小的东西。

倍增

必须从头也就是 \(0\) 位置开始倍增,如果不是 \(0\) 零位置可以巧妙转化。

P6619 [省选联考 2020 A/B 卷] 冰火战士

类比课内数学题求 \(\max\limits_x\{\min\{f(x),g(x)\}\}\),一般都是两个函数交点部分吧。本题冰和火的分别能量和关于温度一个单增,一个单减。显然就是交点处可以最大化最小值,树状数组倍增维护一下即可。注意如果不是严格有一个整交点,要求出两边分别最逼近的整点。

KD-Tree

KD-Tree的精髓就在于剪枝,我们要设计出一个良好的估价函数。
KD-Tree非强制在线的插入,可以提前插入,然后打上激活标记。
下面给出一个例子,\(f_i=\min\{\sqrt{f_j+(A_i-A_j)^2}~|~0\le j<i,A_i \ge B_j\}\)
这是一个类似于 CDQ 分治斜率优化的问题,可以用 KDT 来查询。
加点 \((A_i,F_i^2)\),对应估价函数为 \(y_p+\max(x-x_1(p),x_0(p)-x,0)^2\)

P2093 [国家集训队] JZPFAR

这里的估价函数就是记录每个节点各个方向延伸的最大长度,然后可以计算出每个维度的理论最远距离。用优先队列记录前 \(k\) 大,如果已经满了的话,进入一个节点条件就是理论最大值要大于队列中的最短距离。根据理论最远距离决定先走左孩子还是右孩子。

P4148 简单题

维护二维带修改求和。如果是可以离线的话像这种维护带修改二维信息可以用 CDQ 分治。但是本题在线。
KDT 如果动态加点可能会造成不平衡,于是我们引入平衡常数,如果最大的子节点的大小超过平衡常数倍的当前节点大小那么就暴力重构。重构就按照中序遍历找出所有子节点然后重构。

平衡

来点科技,这玩意儿也就看一个乐,反正我是不会去用的,除非考试没时间了/wul。

pb_ds

首先是头文件

#include <ext/rope>
using namespace __gnu_cxx;  

定义:crope a,b[maxn];
\(\operatorname{push\underline~back}\) 末尾添加
\(\operatorname{insert(p,x)}\) \(p\) 位置后面加字符串 \(x\)
\(\operatorname{erase(p,x)}\) 删除 \(p\) 后面 \(x\)
\(\operatorname{replace(p,x)}\) 从位置 \(p\) 开始替换为 \(x\)
\(\operatorname{substr(p,x)}\) 提取 \(p\)\(x\)
\(\operatorname{at(x)}\) 访问第 \(x\)
注意操作 2 4 都不包含 \(p\)

可并堆

可持久化左偏树

每个合并的时候,新建一个节点复制一份即可。

int merge(int x, int y) {
  if (!x || !y) return x + y;
  if (v[x] > v[y]) swap(x, y);
  int p = ++cnt;
  lc[p] = lc[x];
  v[p] = v[x];
  rc[p] = merge(rc[x], y);
  if (dist[lc[p]] < dist[rc[p]]) swap(lc[p], rc[p]);
  dist[p] = dist[rc[p]] + 1;
  return p;
}

P2483 【模板】k 短路

先对于 \(n\) 建立最短路树,最短路径显然是最短路树上的路径,由于我们是要求 \(k\) 短路,所以需要用非树边进行拼凑。走出来的应该是一条树边与非树边交错的路。我们设 \(\Delta w\) 为加入一条非树边所用额外代价,\(\Delta w=dis_v+e_w-dis_u\)

我们现在有两种操作,在边序列末尾加入边或者替换边。替换某条边则可以在末尾边起点以及其祖先那里选择非树边进行替换。加入则则是在末尾边的终点以及其祖先上选择非树边加入。用一个优先队列保存候选答案即可。

时间复杂度 \(O((n+m)\log n+k\log k)\)

ODT

在数据随机,且有大量区间赋值操作的时候可以用。
原理就是用 set 来维护连续段。
set 的每一个元素都是一个以 \(l\) 为关键字比较的结构体,存着区间和对应的值。
首先是 split 操作,我们要把某个包含 \(pos\) 的区间分成 \([l,pos-1],[pos,r]\)。用 lower_bound 函数寻找即可,记得特判。
assign 区间推平操作。一定要注意先 split(r+1),再 split(l)。然后直接删除这一段,最后插入新的即可。
区间加,就是暴力遍历然后 \(+\)

posted @ 2024-02-27 12:02  Mirasycle  阅读(9)  评论(0编辑  收藏  举报