寒假day2 2.3 ds

讲师:杨宁远,NOI2022Au,rk20,from 成都七中

DS

  • STL

  • LCA(倍增、欧拉序、四毛子优化欧拉序)

  • 四毛子

  • 笛卡尔树(四毛子求RMQ)

  • 二叉堆

  • 启发式合并

  • 左偏树

  • 树状数组

  • 线段树

  • 哈希

  • 字典树

  • Splay

  • Treap

  • 替罪羊树

list

auto 定义指针。

*i 访问元素。

prev(i) next(i) 访问前驱、后继的

rbrgen rend 含义相反。

front back 放回头元素和尾元素。

insert(iterator,value),会在迭代器前插入元素。

erase(iterator),删除元素。

a.swap(b):O(1)

merge 把两个有序的 list 合并。a.merge(b)

vector

a.resize(n),强制把 a 的大小变成 \(n\)。往后加 \(0\)

原理:倍增数组

删除元素空间不会变小。

shrink_to_fit 强制把空间变为 fit ,O(元素个数)

支持随机类型访问

insert erase O(后面元素个数)

a.swap(b) O(1)

set

要求给定元素可以比较

结构体内自定义排序

bool operator <(const node &x)const{

}

erase(值或迭代器)

find(值) 返回迭代器 不存在——set.end()

count(值) 返回0/1

lower_bound upper_bound 返回迭代器

复杂度 \(O(\log)\)

multiset

count(值) 复杂度 O(log+元素个数)。

unordered_set

C++11 哈希实现

无序 复杂度 O(1)

map/unordered_map

访问空下标会创建元素 应该用 mp.find(x)==mp.end()) 进行判断

lower_bound upper_bound 返回迭代器

queue

空间常数大。

a.swap(b) O(1)

deque

时空常数巨大

底层实现用两个 vector 拼接起来

存在 shrink_to_fit

支持下标访问

建议用 list 或自己实现 deque

priority_queue

本质是二叉堆,实现是 vector

加入删除复杂度 \(O(\log)\),常数小。类似 BIT。

ST表与RMQ

\(f_{i,k}=min(a_{i\sim i+2^k-1})\)

递推:\(f_{i,k}=min(f_{i,k-1},f_{i+2^{k-1},k-1)\)

询问:\(min(f_{l,k},f_{r-2^k+1,k-1})\)

可以用于区间线性基。

__lg(x) 返回 \(\lfloor\log x\rfloor\)

倍增求LCA

\(p_{u,k}\) 表示 \(u\) 往上跳 \(2^k\) 步到哪。

\(p_{u,k}=p_{p_{u,k-1},k-1}\)

把较深的跳到同一高度。

注意判断跳完以后是不是一个点。

只要不会相遇就一直跳。

最后多跳一步。

RMQ求LCA

记录 dfs 序(可重)。

记录每个节点的 dfn

两个节点的 LCA 一定在欧拉序中两点 dfn 中间一段区间,是深度最小的。

用 ST 表解决(深度最小 \(\rightarrow\) RMQ)。

预处理 \(O(n\log n)\) 询问 \(O(1)\)

四毛子求LCA

+1-1RMQ

分块,分 \(\log n/2\) 块。

可以发现,欧拉序相邻的深度差为 \(1\),利用四毛子。

\(O(n)\) 预处理,\(O(1)\) 查询。

笛卡尔树

不断找最小值,把区间分裂,划分左右子树。

增量法规建笛卡尔树。

假设求出了 $1\sim i $ 的笛卡尔树,可以发现,\(i\) 号点一定在最靠右的链上,且一定没有右儿子,根据定义,这是显然的。

不可能是某个时刻走左儿子得到的。

建树

如果 \(i+1\) 号比 \(i\) 号大,右儿子。

如果小,往上跳,直到跳到某个点比 \(i+1\) 号大。

对于这个点,令 \(i+1\) 号为它的右儿子,原来的儿子链为 \(i+1\) 号的左儿子。

均摊线性。

性质:lca为区间最值。

构建的本质:维护右链。

四毛子求RMQ?

upd:

二叉堆

insert:向上更新。

erase:将根节点与数组最后一个叶子节点交换后向下调整。

应用:排序 动态维护最值

中位数

用两个堆 small large

不断维护大小平衡

始终尽量维护 small.size()<=large.size()<=small.size()+1

序列合并

二分做法:二分前 \(N\) 小的数中最大的一个,发现对于 \(a_i\) ,合法的 \(j\) 单调不降,双指针做。

堆:对每一个 \(a_i\) 加上 \(b_1\) 后放入堆里,对于最优的求出后继状态(把 \(b_j\) 向右扩展),放入堆里。

左偏树

特殊的二叉堆。

dist:从一点走右链能走多少步

左儿子的 dist 大于右儿子的 dist 。

右链是 \(\log\) 级别。

类似完全二叉树向左偏,而完全二叉树的右链是 \(\log\) 的。

启发式合并

\(n\) 个堆合并,复杂度是 \(O(n\log^2 n)\)

左偏树的合并是严格 \(\log\) 的。

int merge(int x,int y){
    if(!x | !y) return x | y;
    if(val(x) > val(y)) swap(x, y);
    r(x) = merge(r(x), y);
    if(dst(l(x)) < dst(r(x))) swap(l(x), r(x));//保证满足左偏树的性质
    dst(x) = dst(r(x)) + 1;
    return x;
}

不会左偏树。

从归并排序的角度考虑合并

罗马游戏

对每个人用并查集维护在哪个团,用左偏树维护团。

删除堆顶元素:合并左子树、右子树,标记死亡。

Monkey King

通过此题观察左偏树类题的特征:往往类似于并查集,但一般会查询并查集内最值,并对最值有所更改,这是并查集无法维护的,所以会采用左偏树维护。

武力值减小可以视作把原武力值删去,加入一个原武力值一半的节点。

树状数组

\(i\) 号节点维护 \(i-2^{lowbit(i)}+1\sim i\) 的总和。

令所有 \(i\oplus 2^t\) 成为 \(i\) 的儿子(\(t<lowbit(i)\))。

区间加、区间求和

问题:区间价,查询前缀和

\(b_i=a_i-a_{i-1}\),当区间加时,发现 \(b_l+=v,b_{r+1}-=v\)

可以发现,\(b_1+b_2=a_1-a_0+a_2-a_1=a_2\)

所以,\(a_i=\sum\limits_{j=1}^ib_j\)

考虑查询前缀和。

发现:

\(s_i=\sum\limits_{j=1}^ia_j=\sum\limits_{j=1}^i\sum\limits_{k=1}^jb_k\)

发现 \(b_k\) 产生了 \(i-k+1\) 次贡献。

所以 \(s_i=\sum\limits_{j=1}^ib_j\times (i-j+1)=\sum\limits_1^ib_j\times (i+1)-\sum\limits_1^ib_j\times j\)

维护 \(b_j\times j\)

二维树状数组

loj133

单点加,矩形查。

没听。

也可以矩形加,矩形查。

线段树

必须满足标记具有分配律。

方差

维护区间和 & 区间平方和。

维护区间平方和时,采取完全平方公式展开后提取公因式发现一个比较容易维护的式子。

哈希

追求 \(O(1)\) 修改,\(O(1)\) 查询。

取一个不规则的函数,使得 \(f(data)\rightarrow 0\le int\le 10^6\)

取模是一个常见的哈希函数。

对于字符串,取 base ,使字符串变成 base 进制下的数:\(f(s)=\sum\limits_0^n(s_i-'a'+1)\times base^i\)

太大可以取模。

如果数据个数过多,会发生哈希冲突

对于 \(a\ne b\),存在 \(f(a)=f(b)\)

\(\sqrt{n}\) 落在 \(n\) 的范围内,存在大概 \(\frac{1}{2}\) 的概率使得存在冲突。

ln 的求和可以泰勒展开(求导)或微积分。

面对冲突,应人为修改 \(f()\)

字符串哈希

比较 \(l\sim r\)\(l_1\sim r_1\) 是否相同。

双哈希:取 \(p_1\)\(p_2\) ,记录 pair ,fi 表示对 \(p_1\) 取模的结果,se 表示对 \(p_2\) 取模的结果。

自然溢出:易卡。

字符串哈希需要选取两个质数进行双哈希。

cc_hash_table gp_hash_table

unordered_map 较慢。

loj DNA序列

如果 \(k=100\) 需要双哈希,声称两个子串相等当且仅当 \(%p_1\) 相等且 \(%p_2\) 相等。

区间 \(\times -1\) ,求区间最大子段和

竟然上课口胡出来了

维护区间最大和、左起最大和、右起最大和、区间最小和、左起最小和、右起最小和,考虑合并。

面对 \(-1\),考虑最大和变成最小和,最小和变成最大和。

字典树

每条边挂一个字符。

构建考虑对于一个字符串,从上到下来走,如果对于一个字符走不到,就新建。

建议参考 OI-Wiki 。

Secret Message G

考虑对于信息建字典树,并且记录有多少信息在节点截止以及有多少信息经过节点。对于暗号,从树根向下走,统计经过多少完整信息(路径上节点权值之和),当截止后,统计有多少信息经过那个点,两者相加即为答案。

平衡树

参考

!!!

平衡二叉查找树,尽可能把二叉树两棵子树大小平衡一下。

Splay Treap 替罪羊树 红黑树 AVL(没用)

最常用:Treap

Splay 只有在 LCT 中用到

替罪羊树只有在 K-DT中用到

Splay

通过不断将节点旋转,使得仍满足 BST。

不能持久化。

\(O(\log n)\)

旋转本质:某个节点上移一个位置。

保证:

  • 中序遍历不变

  • 信息正确有效

  • root指向旋转后的根

左旋:左边变成右边

右旋相反

maintain 类似 push_up

Splay 操作:不断对一个节点 rotate

Treap

treap=tree+heap

treap 每个节点额外存储一个关键值,根据关键值调整结构。

如果随机生成关键值,树高期望 \(O(\log)\)

合并 \(O(\log)\)

fhq-Treap

核心:分裂、合并

merge:合并两个 treap

先决条件:\(i\in x,j\in y,i<j\)

split:一个 treap 分裂成两个

自己看吧

替罪羊树

用于 KDT

文艺平衡树

以每个数下标作为关键字

先 split(l-1)->split(r)->打tag 子树翻转

kmp

对于 \(1\sim i\),求出 \(nxt_i\),即最大的 \(k\) 满足 \(k\le i,1\sim k=i-k+1\sim i\)

\(nxt_{i+1}\leq nxt_i+1\)

动物园

  • 没听
posted @ 2024-02-03 11:19  BYR_KKK  阅读(16)  评论(0编辑  收藏  举报