欢迎


欢迎来到
图标是清爽葡萄柚汽水(确信)。
LCA,树的一些性质,点分治,树上莫队,DFS 序与欧拉序,笛卡尔树,树上启发式合并,Kruskal 重构树,树链剖分。
1 | void dfs(ll root, ll dad) { |
1 | #include <bits/stdc++.h> |
树上任意两节点之间最长的简单路径即为树的「直径」。
上面的性质在存在负权时依然成立。
在树中,如果节点 \(x\) 作为根节点时,从 \(x\) 出发的最长链最短,那么称 \(x\) 为这棵树的中心。
三个等价定义:
性质:
只要路径信息能由“端点到分治中心”的摘要合并得到,并且能在每层快速查询/插入这些摘要,那么点分治就能处理整棵树所有简单路径的信息。算法框架如下:
1 | solve(当前连通块): |
注意在重新选择根节点之后一定要重新计算子树的大小。
1 | #include <bits/stdc++.h> |
利用下面的欧拉序性质,转化为普通莫队。
1 | #include <bits/stdc++.h> |
对树做
DFS,每个节点第一次被访问时,记录下它的编号,得到的序列就是
DFS 序。通常用 dfn[u] 表示节点
u 是第几个被访问的。
sz[u] 为以
u 为根的子树大小,那么子树 u 对应区间:
[dfn[u], dfn[u] + sz[u] - 1]。u 是 v
的祖先,当且仅当:dfn[u] <= dfn[v] <= dfn[u] + sz[u] - 1。对树做
DFS,每次进入一个节点时记录一次,离开这个节点时再记录一次,得到的序列就是欧拉括号序。每个节点会出现两次,整个序列长度是
2n。
设 st[u] 节点 u 第一次出现的位置,ed[u]
节点 u 第二次出现的位置。
u
在欧拉序中对应:[st[u], ed[u]]。这个区间里每个节点出现了两次。(u, v),假设
st[u] <= st[v],令 p = LCA(u, v):
p == u,路径对应区间
[st[u], st[v]]。p != u,路径对应区间
[ed[u], st[v]],再加上 p。对树做
DFS,每次经过一个节点时都记录一次,得到的序列就是欧拉环游序。整个序列长度是
2n-1。
对树做
DFS,每次到达一个节点就记录它的编号,同时记录它的深度。这样得到两个序列:euler[]
节点编号序列,长度 2n-1;depth[]
对应节点的深度,长度也是 2n-1。
同时记录每个节点第一次出现在 euler[]
中的位置 first[u]。
对于任意两个节点 u 和 v,假设
first[u] <= first[v],那么:
u 和 v 的 LCA,就是
euler[first[u] .. first[v]]
这段区间中,深度最小的那个节点。也就是说,LCA
问题变成了 区间最小值查询(RMQ)。
笛卡尔树是一种特殊的二叉树,每一个节点由一个键值二元组构成,并且同时满足以下两个性质:
经常用于最值维护、最近公共祖先问题。例如柱状图找面积最大的矩形。Treap 维护的也是笛卡尔树。
1 | int cartesian_build(int n) { |
例如计算 \(\sum_{d(x)=d(y),1\le x<y\le n}d(y)-d(lca(x,y))\)。
\(O(n\log n)\) 版:
1 | vector<ll> e[N]; |
原图中两个点之间的所有简单路径上最大边权的最小值 = 最小生成树上两个点之间的简单路径上的最大值 = Kruskal 重构树上两点之间的 LCA 的权值。
也就是说,到点 x 的简单路径上最大边权的最小值 \(\leq val\) 的所有点 y 均在 Kruskal 重构树上的某一棵子树内,且恰好为该子树的所有叶子节点。
注意重构树新建了节点,初始化范围为 \(n+m\)。
1 | sort(h + 1, h + 1 + m, cmp);//按照边权排序 |
把一棵树上的节点重新编号,使得“树上的一条路径”和“一棵子树”都能变成数组上的连续区间。然后就可以用线段树、树状数组等维护区间加、区间求和、区间最大值。
从某个节点开始,一直沿着重儿子往下走,形成一条链,叫重链。每条重链有一个链顶
top。
我们做第二次 DFS
时,优先遍历重儿子,得到的节点编号顺序叫 DFS 序,记为
dfn[u]。
这样会得到两个重要性质:
dfn
是连续的。dfn 也是连续的。
u
对应区间:[dfn[u], dfn[u] + sz[u] - 1]所以树上问题就变成了数组区间问题。
只有链可以直接使用倍增,只有子树可以直接使用 dfs 序。
1 | #include <bits/stdc++.h> |
方向合并:某些题目信息是不可交换的。在树链剖分跳链时,由于 dfn 顺序和真实路径方向可能相反,必须把某些区间的信息“翻转”后再按正确顺序合并。
1 | Info query_path(int u, int v) { |
给定非负整数 \(x\),求三个非负整数 \(a,b,c\) 满足 \(a\times b+c=x\),并使得 \(\max(a,b,c)−\min(a,b,c)\) 尽可能小。输出该最小值。要求约 \(O(\sqrt x)\)。
已知 \(n\) 个交易日中股票的股价(所有价格均以自然对数的形式给出)。可以购买非整数份的股票。共有 \(m\) 个时间区间,本金均为 \(e^k\)。求每个时间区间进行买入卖出后得到的总资产。\(n\le 10^5,m\le 10^5\)。
博弈规则如下:
请你判断 gsh 是否必胜,若必胜,输出 Yes,否则输出 No。
只含有 01? 三种字符的字符串,可以把 ? 变成
0 或 1,求逆序对最多为多少。
预处理前缀 1 个数与后缀 0 个数,先假设都填 0,之后从左向右开始填 1。贡献变化为加后面 0 的个数减前面 1 的个数。
题意:长度为 \(n\)(\(10^9\))的字符串有 \(m\)(\(10^6\))个固定为
#,其余能填任意小写字母。问至少有一个 ccf
和一个 cspark 子串,且至少有一个 ccf 出现在
cspark 前的字符串数量。
期末考试临近了,yz 和 zzw
收到了来自老师的试卷关爱,但是试卷太多了,足足有
\(10^{6}\)
张,他们已经无法排好试卷了。
所以在 zzy 的建议下,他们把这个问题交给了你。
把柱子的点拆成入点出点两部分,它们间的连边的容量就是柱子的高度(能跳出的次数)。
柱子的出点与所有距离 d 以内的柱子的入点和场外点连容量正无穷的边。
源点和有蜥蜴的柱子的入点连容量为 1 的边,场外点和汇点连容量正无穷的边。