Project Euler Problem 918

定义数列 \(\{a_{n}\}\) 满足 \(a_{1} = 1\), 对于 \(n \geqslant 1\) 有:

\[ \begin{aligned} a_{2n} &= 2a_n \\ a_{2n + 1} &= a_n - 3a_{n + 1} \end{aligned} \]

「ABC356F」Distance Component Size Query

存在初始为空的点集 \(S\), 进行 \(n\) 次操作, 形如:

\(n \leqslant 2 \times 10^{5}, k, u \leqslant 10^{18}\).

树上背包

这是最普遍的情况, 从树中选取 \(m\) 个结点, 最大化它们的价值之和.

符合直觉地, 设 \(f_{u, i}\) 表示从以 \(u\) 为根的子树中选取了 \(i\) 个结点的最大价值之和. 转移是显然的:

这样转移时间复杂度即为 \(\Theta(nm)\).

某 0/1 背包 Trick

有 \(n\) 个物品, 第 \(i\) 个重量为 \(a_{i}\), 求重量不大于 \(c\) 的情况下能够装下的最大重量.

\(n, m = \max\{a_{i}\} \leqslant 2 \times 10^{4}, c \leqslant 10^{9}.\)

The 2024 ICPC St. Petersburg

Solution of “The 2024 ICPC St. Petersburg”, and it also be kown as “The 3rd Universal Cup. Stage 1: St. Petersburg”.

The 2024 ICPC North America Championship

Solution of “The 2024 ICPC North America Championship”, and it also be kown as “The 3rd Universal Cup. Stage 0: Trial Contest”.

2024 年 2~3 月趣题记录

给定一颗 \(n\) 个点的树, 求黑白染色的方案数, 使任意一条路径上的黑点数量不超过 \(2\).

\(2 \leqslant n \leqslant 3 \times 10^{5}, \mathrm{mod} = 998244353\).

引理

证明

长链剖分

考虑将重儿子设为链最长的那一个,剖分是简单的,可以做到 \(\Theta(n).\)

这样剖分有一些性质:

由于长链长度之和为 \(n\),所以我们可以预处理每条链顶端节点的 \(l\) 级祖先,\(l\) 取遍不大于链长的所有整数。并且我们还可以存下每条链的所有节点,挂在链顶。

虚树

在一些问题中,我们只关心关键点的信息,同时我们需要维护他们之间的树形结构,于是可以想到将它们和它们之间的 \(\rm LCA\) 拉出来建一颗树,这棵树就被称作虚树。

不难发现这样一定是对的,并且设关键点数量为 \(k\),则虚树内点数不超过 \(2k\)。

首先建虚树。

缩点 & 割点 & 割边

若图 \(G\) 中存在一个子图 \(G'\),\(G'\) 中任意两点均可互相到达,则称 \(G'\) 为 \(G\) 的一个强连通分量。若 \(G'\) 不存在一个严格子图为强连通分量,则 \(G'\) 为 \(G\) 的极大强连通分量。

参考代码:

线段树相关杂题

扫描线模板题,从小到大枚举横线的纵坐标,枚举到矩形下界就给这条边的左右端点构成的区间 \(+1\),否则 \(-1\),用线段树维护,当前线段树有值的结点个数就是这个纵坐标的贡献。

当然,本题还需要动态开点,所以需要维护区间内有效点的数量。

考虑用扫描线,维护区间最大值即可。

线性筛

考虑如何筛出 \(n\) 以内的所有素数,显然有一个最简单的想法,即将所有已求出的质数的倍数标记,表示它们不为质数,并且从小到大遍历到数 \(i\) 时,若它没有被标记,则 \(i\in Prime\)。 在此算法中,每个数都被自己所有本质不同的质因子筛了一次,所以总复杂度为 \(\Theta(n\ln \ln n)\)。

线性同余方程

考虑如何求解以下方程 :

显然可以转化为 :

有裴蜀定理 :

对于不定方程 \(ax+by=c\),其有解当且仅当 \(\gcd(a,b)|c\)。

可以发现上述方程有解当且仅当 \(\gcd(a,b)=1\),故可以转化为 :

简单的代数反演

\(\large\rm Proof:\)

将上式带入下式得 \(:\)

整理得 \(:\)

根据组合恒等式 \(\dbinom{i}{j}\dbinom{j}{k}=\dbinom{i}{k}\dbinom{i-k}{j-k}\) 得 \(:\)

交换枚举顺序得 \(:\)

生成函数强化训练

考虑 \(\rm EGF\) 的组合意义,可以发现若设任意图方案数的 \(\rm EGF\) 为 \(F(x)\),连通图的 \(\rm EGF\) 为 \(G(x)\),显然任意图计数可以被看做先将 \(n\) 个点分进若干个非空子集,然后做连通图计数的方案数,那么根据之前的结论,有 :

生成函数

序列 \(a\) 的普通生成函数 \(\rm(Ordinary\ Generating\ Function,OGF)\) 被定义为形式幂级数 :

\(a\) 既可以是有穷序列也可以是无穷序列,\(x\) 不具有实际意义,仅作为一个形式上的变量参与运算。

加减不必多说 :

则有:

欧拉回路

  • 欧拉路径:如果图中的一个路径包括每个边恰好一次,则该路径称为欧拉路径。
  • 欧拉回路:首尾相接的欧拉路径被称为欧拉回路。

由于每一条边都要经过恰好一次,因此对于除了起点和终点之外的任意一个节点,只要进来,一定要出去,故有如下结论:

杜教筛

杜教筛被用来求积性函数的前缀和。

设有积性函数 \(f(x)\),其前缀和为 \(S(n) = \sum \limits_{i = 1}^{n} f(i)\)。

考虑构造 \(h(x)\) 和 \(g(x)\),满足 \(h = f \times g\)。

推一下式子 :

最短路

有时候我们需要维护一些具有传递性的关系,如相等,连通等。

初始条件往往是给定几组关系,要把所有的关系求出来。

可以把 Floyd 算法做一些调整:

但是 Dijkstra 算法要求所有边权均非负,于是就有了重赋权的技巧。

接下来证明这样做为什么是对的:

首先有定义 :

最小生成树

最常用且大部分情况下效率最高的最小生成树算法。

将边按长度从小到大排序,每次贪心选择最短的一条边,若这条边连接的两个点还为连通,则将两个点所在的连通块用这条边连接,连通块用并查集维护。

时间复杂度 \(\Theta(m\log m)\)。

在稠密图上复杂度最优的最小生成树算法。

数列分块

考虑一些在序列上的区间操作,如区间加和区间求和。

直接暴力做单次操作复杂度是 \(\Theta(n)\),总时间复杂度 \(\Theta(qn)\).

具体实现如下:

对每个块维护整块加的值,查询的时候加上即可。

时间复杂度 \(\Theta(n\sqrt{n})\)。

差分约束

差分约束是用来解形如 \(x_{1}-x_{2}\leqslant k\) 的若干个方程的算法。

另外,如果有负环则无解。

时间复杂度 \(\Theta(\rm SPFA)\)。

需要注意的是,最短路求的是以起始点为基准的最大解,而最长路求的是以起始点为基准的最小解。

多项式

插值问题形如 :给定 \(n\) 个点的坐标 \((x, y)\),要求出一个经过所有点的最低次多项式。

容易构造出 \(f(x)\) 即为下式 :

正确性显然。

容易证明多项式次数至少为 \(n - 1\) 才可以确保穿过 \(n\) 个点,故最优性得证。

考虑分类讨论 :

复杂度分析

主定理 \(\rm(Master\ Theorem)\) 被用来求解一类递归算法的时间复杂度,假设有递推式形如:

那么根据主定理,有 :

反悔贪心

反悔贪心是对贪心策略的一种优化,有些时候贪心策略是错的,但如果可以撤销之前的操作,那么就会变为正确的。

另外,有一种观点认为反悔贪心的本质是贪心模拟费用流,我认为是有一定道理的,这种观点也有利于解释反悔贪心的正确性。

反悔贪心板子题。

反悔贪心板子题 \(\times 2\)。

原根和阶

称使得 \(a^{t}\equiv 1\pmod p\) 成立的最小正整数 \(t_{min}\) 为 \(a\) 对模数 \(p\) 的阶,记为 \(\delta_{p}(a)\)。

有如下定理 :\(g_{min}\lt p^{\frac{1}{4}}\)。

十二类基本组合问题

小球入盒问题是所有组合问题的基础,它计算的是形如 “\(n\) 个球放进 \(m\) 个盒子的方案数” 的一类问题,在这类问题中一般存在三种限制 :

  • 小球是否有编号。
  • 盒子是否有编号。
  • 盒子中球的数量是无限制,至少一个还是至多一个。

理由同上,方案数为 :

决策单调性

假设有 \(p_{1}\leqslant p_{2}\leqslant p_{3}\leqslant p_{4}.\)

假设一个问题满足四边形不等式,那么它的决策点有单调性。

Proof:

根据最优决策的定义写出这个位置的转移式 :

两不等式相加,得到 :

Z 算法

\(Z\) 算法在国内也被某些人称为 \(\rm ExKMP\)(虽然我不知道它跟 \(\rm KMP\) 算法有什么关系)。

\(Z\) 算法用于求字符串 \(S\) 的 \(Z\) 函数,其定义为 \(z_{i}=\rm LCP(S,S_{i\sim n})\)。

Prufer 序列

\(\rm Prufer\) 序列可以将一颗结点数为 \(n\) 的有标号无根树用一个长度为 \(n-2\),值域为 \([1,n]\) 的数列唯一表示,即有标号无根树和 \(\rm Prufer\) 序列呈双射关系。

具体来说,有标号无根树的构建方式如下:

Manacher 算法

\(\rm Manacher\) 是用于求解一个字符串中所有回文子串的算法。

首先,回文子串的数量级是 \(n^{2}\) 的,所以要考虑如何将所有回文子串快速地表达出来,容易发现如下性质:

KMP 算法 & KMP 自动机

  • 字符串 \(S\) 的真前/后缀为非自身的前/后缀。
  • 字符串 \(S\) 的 border为 \(S\) 的公共真前/后缀。
  • 字符串 \(S\) 的最长 border 为 \(\pi\),对于 \(S\) 的每个前缀 \(S_{1 \sim i}\) 令 \(\pi_{i}\) 为其最长 border,\(\pi\) 函数就是所谓的前缀函数。

AtCoder Beginner Contest 247

考虑枚举右端点 \(R\),计算对于每个 \(R\) 有多少个左端点 \(L\) 满足条件。

左端点需要满足的条件如下:

转化条件:

时间复杂度 \(\Theta(n)\)。

观察到每个点度数都恰好为 \(2\),于是连边后构成若干个环。

AtCoder Beginner Contest 214

考虑枚举最大边,分别计算从两个端点出发能走到多少个点。

可以发现并查集能够很好地拟合这个过程,于是将边从小到大加入,每次合并的两个点集即为所求。

时间复杂度 \(\Theta(n \log n)\)。

总时间复杂度 \(\Theta(n^{2})\)。

AtCoder Beginner Contest 213

考虑使用 \(\rm bfs\)。

观察到每一步的代价都是 \(0\) 或 \(1\),于是建立双端队列。每次迭代取队头,如果走了代价为 \(0\) 的边就将出点加入队首,否则加入队尾。

时间复杂度 \(\Theta(nm)\)。

考虑使用状压 \(\rm DP\)。

AtCoder Beginner Contest 212

考虑记 \(f_{i, u}\) 表示走 \(i\) 步到 \(u\) 的方案数。显然有转移式:

状态数为 \(nk\),转移的时间复杂度为 \(\Theta(n)\),于是总时间复杂度为 \(\Theta(n^{2} k).\)

考虑到删除的边数很少,于是将转移方程改写为:

AC 自动机

定义 fail 为广义的 \(\pi\) 函数,即对于 \(n\) 个字符串,\(fail_{i,j}=\max\limits_{k=1}^{n} \pi(S_{k}\#S_{i,1\sim j})\)。

2021 年计数题康复训练

  • 将要计数的总体分成很多个部分,每个部分分别计算贡献。
  • 有些时候一个集合 \(S\) 联合产生贡献,为了避免算重,可以将这个集合的贡献放在其中的某个元素上计算,比如标号最小的那一个。
  • 可以考虑划分阶段,每次加入一个元素时计算答案增量。
  • 如果需要计算在一个大的部分中选…

2-SAT 问题

\(2-\rm Sat\) 问题是求解某个 \(\rm bool\) 方程组,方程组中的每个方程被描述为:\(x_{i}\) 为真\(/\)假 或 \(x_{j}\) 为真\(/\)假。

对树上每个点建立一个 \(\rm bool\) 变量,取值为 \(1\) 表示这个点选。

「省选联考 2021」矩阵游戏

首先可以发现,若不考虑 \(a_{i} \leqslant 10^{6}\) 的限制,那么构造一组合法解是容易的。

同时可以发现,如果对一行或一列进行 \(+1, -1, +1, -1, \cdots \) 操作,那么得到的 \(a_{i}\) 还是合法的。

「省选联考 2021」图函数

首先考虑分析 \(f(u, G)\),可以观察出一些性质 :

  • 会产生贡献的 \(v\) 必须满足 \(v \lt u.\)
  • \(v\) 与 \(u\) 会产生贡献当且仅当 \(u\) 和 \(v\) 可以仅通过大于 \(v\) 的点互达。

有两个显然的性质 :

「省选联考 2021」卡牌游戏

可以证明,若进行了操作 \(a_{i} \to b_{i}\),那么 \(\forall j \lt i\),操作 \(a_{j} \to b_{j}\) 都进行了。因为如果存在 \(j \lt i\),\(a_{i} \to b_{i}\) 执行了,但 \(a_{j} \to b_{j}\) 没执行,那么 \(a_{i} \to b_{i}\) 执行就没有意义,因为 \(a_{j}\) 还是会作为最小值。

「Codechef」Children Trips

你在一颗树上旅行。

树由 \(n\) 个点,\(n - 1\) 条边构成,每条边的长度 \(w\) 是 \(1\) 或 \(2\)。

给定这棵树和 \(q\) 个询问,每个询问给出 \(u,v,l\),请求出至少需要多少天。

「CF690A」Collective Mindsets

你是一只僵尸。

你现在和 \(n - 1\) 个僵尸站在一起分配大脑。你的编号是 \(1\),其他僵尸编号 \(2 \sim n\)。

给定 \(n\),现在你想活下去,并且获得至少 \(m\) 个大脑,请问至少需要多少大脑才能满足?

首先考虑 \(m = 1\) 的情况。

(带权)并查集

并查集可以用来维护一类具有传递性的关系,维护形如将 \(x\) 与 \(y\) 所在集合合并和询问 \(x\) 和 \(y\) 是否在同一个集合的操作。

对于普通并查集来说,有路径压缩和按秩合并等优化,路径压缩一般实现如下 :

按秩合并则取点数小的连向点数大的即可。

(可持久化) Trie 树

顾名思义,字典树就是支持像字典一样加入和查询字符串的树形数据结构,实现如下:

在很多问题中,字符串仅由 \(0\) 和 \(1\) 构成,这些问题存在一定的共性。

考虑如下问题,给一些数,维护它们的异或和,支持插入,删除和全局 \(+1\)。

与线段树合并类似,实现如下: