树上莫队-笔记 /「SPOJ 10707」COT2-题解
通过SPOJ 10707 COT2-Count on a tree II这道题目来学习一下 树上莫队
当需要离线查询 树上 的多区间问题时,可以使用 树上莫队 来解决
主要通过 欧拉序 将树转化为一条链,然后在链上执行普通莫队的操作
通过SPOJ 10707 COT2-Count on a tree II这道题目来学习一下 树上莫队
当需要离线查询 树上 的多区间问题时,可以使用 树上莫队 来解决
主要通过 欧拉序 将树转化为一条链,然后在链上执行普通莫队的操作
通过AtCoder 1219 歴史の研究这道题目来学习一下 回滚莫队
回滚莫队 适用于容易进行add
操作,而不容易实现del
的情况
通过莫队的分块,指针移动的思想,可以让左指针进行回滚操作, 近似 达到del
的效果
通过Luogu P1903 数颜色/维护序列这道题目来学习一下 带修莫队
顾名思义,带修莫队 不仅要支持普通莫队的查询操作,还要支持数据中途的修改
比如这道题目,需要实现以下目标
达到这个目标,可以在普通莫队的基础上加一个时间维度,实现 带修莫队
这篇文章来记录一下莫队算法时间复杂度的简单(不严谨)计算
首先分析一下莫队算法的时间复杂度有哪些方面构成
Query
数组的排序$dsu\ on\ tree$($disjoint\ set\ union\ \text{on tree}$)算法,也称 __树上并查集__。使用了并查集的按秩合并(启发式合并)的方法,结合 树链剖分 中的 轻重儿子划分 ,对 树上暴力统计 进行了优化。使用这个算法需要满足以下两个条件:
左偏树($Leftist\ Tree$),是一种 __可以合并的堆状结构__,支持以下操作
树状数组($Binary\ Indexed\ Trees$)是一个维护__前缀和__的数据结构,需要支持以下操作
模板地址: GitHub
1 | LL gcd(LL a, LL b) { |
lcm(a, b) = a / gcd(a, b) * b
目标: 寻找一对整数$(x, y)$,使$ax+by=gcd(a,b)$
1 | void exgcd(LL a, LL b, LL& d, LL& x, LL& y) { |
$ax+by=c$且$x,y$全为正整数,则当且仅当$gcd(a, b)|c$
1 | bool vis[maxn]; |
1 | void getprime(int n) { |
$$
\pi(x) \sim \frac{x}{\ln x}
$$
原理:费马小定理
若$a^{n-1}\equiv 1\pmod n$,$a$取值越多,可以近似认为$n$为质数
使用二次探测定理改进卡卡迈尔数(合数$n$对于任何正整数$b$,都满足$gcd(b, n)=1\ \ b^{n-1}\equiv 1\pmod n$)的bug
1 | LL Random(LL n) { return (LL)((double)rand() / RAND_MAX * n + 0.5); } |
$$
(a + b)\bmod n = ((a\bmod n) + (b\bmod n))\bmod n\\
(a - b)\bmod n = ((a\bmod n) - (b\bmod n) + n)\bmod n\\
ab\mod n = (a\bmod n)(b\bmod n)\bmod n
$$
1 | LL mul_mod(LL a, LL b, LL n){ |
1 | LL pow_mod(LL a, LL p, LL n) { |
使用位运算:
1 | LL pow_mod(LL a, LL p, LL n) { |
$$\varphi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})…(1-\frac{1}{p_k})$$
$\varphi(n)$表示不超过$n$且与$n$互质的整数个数
1 | int euler_phi(int n) { |
1 | int phi[maxn]; |
若$gcd(a, n) = 1$,则$a^{\varphi(n)}\equiv 1\pmod n$
若$p$为质数,则$a^{p-1}\equiv 1\pmod p$
若$p$为质数,则$(p-1)!\equiv -1\pmod p$
$$a\div b\bmod n = a\times b^{-1}\bmod n$$
$b^{-1}$称为$b$在模$n$意义下的逆元
使用费马小定理, $a^{-1} = a^{n - 2}$
递归求解
1 | LL inv(LL a, LL n) { |
1 | int inv_table[maxn]; |
$ax\equiv b\pmod n$可以化为$ax+ny=b$使用扩展欧拉定理解决
求解$x\equiv a_i\pmod {m_i}$满足$m_i$两两互质
1 | LL crt(int n, int* a, int* m) { |
$m_i$不一定两两互质
1 | LL excrt(LL n, LL* a, LL* m) { |
求解$a^x\equiv b\pmod n$满足$n$为质数
1 | int log_mod(int a, int b, int n) { |
整除分块可以对后面的莫比乌斯反演提供很大的优化
通过枚举可以发现$\lfloor \frac{n}{i} \rfloor$的结果会出现分块现象
例如$n=10$时
$\ \ \ i\ \ \ \ 1\ \ 2\ 3\ 4\ 5\ 6\ 7\ 8\ 9\ 10\ 11\ 12\ 13\ …$
$\lfloor \frac{n}{i} \rfloor\ 10\ 5\ 3\ 2\ 2\ 1\ 1\ 1\ 1\ 1\ \ \ 0\ \ \ 0\ \ \ 0\ \ \ …$不难发现,每个块的右端点为$r=\lfloor \frac{n}{t}\rfloor (t=\lfloor \frac{n}{i}\rfloor)$
$$
\mu(n) =
\begin{cases}
1, & n=1 \\
(-1)^r, & n=p_1p_2p_3…p_r(\text{$p_i$为互不相同的质数}) \\
0, & else
\end{cases}
$$
性质:
$$\sum_{d|n}{\mu(d)}=[n=1]$$
$$\sum_{d|n}{\frac{\mu(d)}{d}}=\frac{\varphi(n)}{n}$$
线性筛:
1 | int mu[maxn], vis[maxn]; |
网络流算法相关模板,讲解__以后再说吧__,咕咕咕
n, m, q
点数、边数、问题数x, y
需要合并的两个数ufs[]
并查集find(int)
查找并查集中一个数的祖先unionn(int, int)
合并两个数所在集合
1 | const int maxn = 10010; |
1 | template <typename T> |
1 | template <typename T> |
vector<数据类型> 名;
例 vector<int> a;
a.size();
读取大小a.resize();
改变大小a.push_back(x);
尾部添加元素xa.pop_back();
删除最后一个元素a.clear();
清空a.empty()
询问是否为空(bool类型)a[]
访问元素(可修改)
头文件: #include <queue>
参数: priority_queue<Type, Container, Functional>
Type
数据类型 不可省
Container
容器(vector,deque)默认vector
Functional
比较方式,默认operator <
大根堆
与queue类似
priority_queue<int, vector<int>, greater<int> > q;
使用仿函数greater<>
1 | struct Node |
1 | bool operator < (Node a, Node b) |
x值大的优先级低,排在队前
x相等,y大的优先级低
1 | struct cmp |
有 $n$ 件物品,和一容积为 $V$ 的背包,第 $i$ 件物品的体积为 $w_i$ ,价值为 $c_i$ 。将第几件物品装入,使体积不超过总体积,且价值和最大,求最大价值。
由题意易知状态转移方程: $F_{i,j} = max(F_{i-1,j}\ , F_{i-1,j-w_i} + c_i)$
$F_{i, j}$ 为前 $i$ 件物品放入容量为 $V$ 的背包中最大价值
时间复杂度 $O(n\times V)$ ,空间复杂度 $O(n\times V)$
注意倒序,保证f[n][V]
为结果
1 | for (int i = 1; i <= n; ++i) |
降至一维数组
时间复杂度 $O(n\times V)$ ,空间复杂度 $O(V)$
1 | for (int i = 1; i <= n; ++i) |
有 $n$ 种物品(每种 无限件 ),和一容积为 $V$ 的背包,第 $i$ 种物品的体积为 $w_i$ ,价值为 $c_i$ 。将第几种物品取任意件装入,使体积不超过总体积,且价值和最大,求最大价值。
将01背包第二个循环改为正序即可
状态转移方程:$F_j = max(F_j\ , F_{j-w_i}+c_i)$
1 | for (int i = 1; i <= n; ++i) |
有 $N$ 种物品,和一容积为 $V$ 的背包,第 $i$ 种物品有 $n_i$ 件,体积为 $w_i$ ,价值为 $c_i$ 。将第几件物品装入,使体积不超过总体积,且价值和最大,求最大价值。
状态转移方程:$F_{i,v} = max(F_{i-1,v-k\times w_i} + k\times c_i | 0\leqslant k\leqslant n_i)$
时间复杂度:$O(V\times \sum{n_i})$
1 | for (int i = 1; i <= N; ++i) |
把 $n_i$ 件一种物品化为单独的 $n_i$ 件物品即可
时间复杂度:$O(V\times \sum{n_i})$
框架略
$$
n_i\to 1+2+4+\dots +2^{k-1}+\dots +(n_i-2^k+1)
$$
$$
\sum{n_i}\to \sum{\log_2{n_i}}
$$
时间复杂度:$O(V\times \sum{\log_2{n_i}})$
1 | for (int i = 1; i <= n; ++i) |
有 $N$ 种物品,和一容积为 $V$ 的背包,第 $i$ 种物品有 $n_i$ 件或无穷件,体积为 $w_i$ ,价值为 $c_i$ 。将第几件物品装入,使体积不超过总体积,且价值和最大,求最大价值。
1 | for (int i = 1; i <= N; ++i) |
有 $N$ 件物品,容积为 $V,U$ 的两个背包,每件物品有两种费用,选择物品需要付出两种代价,第 $i$ 件代价为 $a_i,b_i$,价值为 $c_i$。将第几件物品装入,使体积不超过总体积,且价值和最大,求最大价值。
改为二维数组即可
状态转移方程:$F_{v,u} = max(F_{v,u}\ , F_{v-a_i,u-b_i} + c_i)$
$F_{v,u}$ 表示前面的物品付出代价分别为 $v,u$ 时的最大价值
框架略
循环顺序
v = V..0 u = U..0
v = 0..V u = 0..U
有 $K$ 组物品, $V$ 的背包,第 $k$ 组有 $N_k$ 件物品,第 $i$ 件物品的体积为 $w_i$ ,价值为 $c_i$ ,每组中只能选一件物品。将第几件物品装入,使体积不超过总体积,且价值和最大,求最大价值。
1 | for (int k = 1; k <= K; ++k) |
状态转移方程:$F_{i,v} = sum(F_{i-1,v}, F_{i-1,v-w_i})\ \ \ (F_{0,0} = 1)$
1 | f[0] = 1; |
heap[]
堆heap_size
堆大小put(int)
压入一个数get()
弹出堆顶
1 | int heap[maxn]; |
1 | int heap[maxn]; |
n, m, s
点数、边数、源点cnt, head[], edge[], add(int, int, int)
链式前向星dist[]
各点到源点路径长vis[]
记录
1 | const int maxn = 10010; |
n, m, _map[][]
点数、边数、邻接矩阵dist[]
树根到各点路径长pre[]
生成树路径
1 | const int maxn = 101; |
ufs[], find(int), unionn(int, int)
并查集结构edge[]
链式前向星cmp(Edge, Edge)
边排序方案
1 | const int maxn = 1010; |
例:洛谷P3375
pre()
求前缀数组kmp()
匹配字符串
1 | char s1[1000010], s2[1000010]; |
例:洛谷P4305
hash[]
哈希表find(int x)
查找哈希表中 $x$ 的位置push(int x)
将 $x$ 插入到哈希表中check(int x)
查找 $x$ 是否在哈希表中
1 |
|
例:洛谷P3370
1 | typedef unsigned long long ULL; |
1 | typedef unsigned long long ULL; |
1 | typedef unsigned long long ULL; |
1 | typedef unsigned long long ULL; |
n, m, G[][]
点数、边数、邻接矩阵dist[][]
每对顶点间路径长度pre[][]
每对顶点之间路径make()
建图
1 | const int maxn = 110; |
G[][]
邻接矩阵deg[]
度ans[]
欧拉回路n, e
点数、边数
1 | int G[maxn][maxn], deg[maxn], ans[maxn]; |
n, m
点数、边数head, edge[]
链式前向星ans[], ansi
路径、数组大小vis[]
记录make()
建图
1 | int head[maxn]; |
待完成
1 | graph LR |
!!! tldr “题目及注解”
求上图从 $A$ 到 $E$ 的最短距离
$K$: 阶段
$D(X_I, (X+1)_J)$: 从 $X_I$ 到 $(X+1)_J$ 的距离
$F_K(X_I)$: $K$ 阶段下 $X_I$ 到终点 $E$ 的最短距离
倒推:
$$
K=4\qquad F_4(D_1)=3\qquad F_4(D_2)=4\qquad F_4(D_3)=3
$$
$$
K=5\qquad F_3(C_1)=min(D(C_1,D_1)+F_4(D_1),D(C_1,D_2)+F_4(D_2))=min(5+3,6+4)=8
F_3(C_2)
$$
n, m
点数、边数G[][]
邻接矩阵存图dist[]
路径长度pre[]
路径make()
建图
1 | const int maxn = 10010; |
1 | struct Edge |
1 | int q = n, p[maxm]; |
模板:洛谷P3374
tree[]
树状数组lowbit(int)
神奇的函数add(int x, int k)
第 $x$ 个数加上 $k$ sum(int x)
前 $x$ 个数的和
1 | int tree[2000010]; |
1 |
|