NOIP算法学习笔记

第一板块——基本算法

搜索

双向广搜

常见用法

OI-Wiki 双向搜索

补充

  • 双向广搜判无解的效率一般比不上普通广搜

题目

  • P1379 八数码难题
    简要思路:把 \(string\) 的状态存到 \(map\) 中,再把每个位置的可拓展的状态代表出来,跑双向 \(bfs\) 即可

第二板块——数学

位运算

常见用法

OI-Wiki 位运算

组合数学

排列组合

常见用法

OI-Wiki 排列组合

补充

  • \[\begin{pmatrix}n \\m \\ \end{pmatrix} = \begin{pmatrix}n-1 \\m \\ \end{pmatrix} + \begin{pmatrix}n-1 \\m-1 \\ \end{pmatrix} \]

  • \[\sum^n_{i=1} \normalsize\begin{pmatrix}n \\i \\ \end{pmatrix} = 2^n \]

题目

第三板块——图论

拓扑排序

常见用法

OI-Wiki 拓扑排序

题目

P1347 排序
简要思路:按照小于关系拓扑建边,有以下几种情况:

  1. 出现环,则不合法
  2. 图不连通但没出现环,则有无穷多解
  3. 否则,有唯一解

差分约束

常见用法

OI-Wiki 差分约束

补充

  • 差分约束中,跑最短路还是最长路看不等式的符号,一般而言,\(dis_u \le dis_v+w\) 跑最短路,\(dis_u \ge dis_v+w\) 跑最长路

  • 差分约束中,一般情况下(建超级源点 \(0\),初始 \(dis_0 = 0\)),
    如果跑最短路,那么对于 \(i\in[1,n],dis_i \le 0\)
    如果跑最长路,那么对于 \(i\in[1,n],dis_i \ge 0\)
    所以,当题目中要求 \(i\in[1,n],A_i \le 0\),可以直接跑最短路,反之跑最长路

  • 形如 \(A_u =(\le或\ge) x\) 的等式,可以建超级点 \(n+1\),转化为 \(dis_u =(\le或\ge) dis_{n+1}+x\),这里只讨论跑最短路的情况,最长路同理:
    跑最短路后,可以发现,超级点的值也会更新,此时,超级点的值其实为 \(\min_{i\in[1,n] \land A_i确定} A_i\),但我们想要的是 \(dis_0 = 0\)所以 \(dis_i\) 的真实值为 \(dis_i-dis_0\)

题目

  • P4926 [1007] 倍杀测量者
    简要思路:给式子两边都取 \(log\),套差分约束板子即可

  • P5590 赛车游戏
    简要思路:转换为 \(dis_a - dis_b \in [1,9]\),跑差分约束。有几点要注意:
    1.注意判断 \(1\) 能否到 \(n\)
    2.如果有边不在 \(1\)\(n\) 的路径上,就把这些边去掉,不然会影响差分约束的判断

最短路

易错点

  • 假如有负权边,当 \(w\) 为负数时,如果 \(dis_u\)\(dis_v\)\(inf\) 时,\(dis_v\) 就会被更新,所以要么最后判断时不写 "\(==inf\)",要么在松弛时判断 \(dis_u\) 不为 \(inf\)\(Floyd\) 松弛同理)

Bellman-Ford

常见用法

OI-Wiki Bellman-Ford

补充

  • 有边数限制的题可以考虑 \(Bellman-Ford\)
    比如:求从 \(1\) 号点到 \(n\) 号点的最多经过 \(k\) 条边的最短距离
    细节:\(Bellman-Ford\) 有每轮更新有串联效应,比如在一轮中,\(1\) 更新 \(2\) ,更新了的 \(2\) 又更新 \(3\) ,这样就不保证最多经过 \(k\) 条边,所以应再加一维,可用滚动数组优化。

Dijkstra

常见用法

OI-Wiki Dijkstra

题目

  • P1144 最短路计数
    简要思路:松弛时,\(dis_u+w<dis_v\)\(cnt[v]=cnt[u]\)\(dis_u+w=dis_v\)\(cnt[v]+=cnt[u]\),原因显然

  • P1462 通往奥格瑞玛的道路
    简要思路:直接做不容易,考虑二分答案,于是 \(check(x)\) 就要判断能否从 \(1\)\(n\) 经过一些边,边权 \(\le x\),且边权和小于血量。重新建图,跑最短路判断即可

  • P5304 [GXOI/GZOI2019] 旅行者
    简要思路:考虑把点分成两个集合,建两个超级源点分别与这两个集合的点连边,则它们的最短路就是两集合之间最短路的最小值。但任意两点的最小值可能在集合内,此时我们考虑重新划分集合,容易发现,可以枚举二进制中的第 \(i\) 位,为 \(0\) 的放进一个集合,为 \(1\) 的放进另一个集合,这样就一定能找到最小值,原因显然

Floyd

常见用法

OI-Wiki Floyd

补充

  • 注意重边( \(dp_{x,y}=min(dp_{x,y},z)\)

题目

  • P6175 无向图的最小环问题
    简要思路:见 OI-Wiki 最小环

  • P1730 最小密度路径
    简要思路:因为是有向无环图,所以任意两点之间的最小密度路径长度不会超过 \(n\),所以 \(O(n^5)\) 暴力 \(DP\) 即可,再想优化可以参考题解

  • P1613 跑路
    简要思路:数据范围小,考虑 \(Floyd\),由于一次可以跑 \(2^k\),所以我们可以预处理出 \(x\)\(y\) 是否有长度为 \(2^k\) 的边(倍增思想),再跑 \(Floyd\),时间复杂度 \(O(n^4)\)

Johnson

常见用法

OI-Wiki Johnson

分层图

常见用法

OI-Wiki 分层图

题目

  • P4568 [JLOI2011] 飞行路线
    简要思路:板子题

  • P1266 速度限制
    简要思路:对于速度可以建分层图,实现时可以定义 \(dp_{i,j}\) 表示到 \(i\) 时速度为 \(j\) 的最短路,时间复杂度 \(O(V_{max} \times m \times \log m)\)

连通性

常见用法

OI-Wiki 强连通分量
OI-Wiki 双连通分量
OI-Wiki 割点和桥

补充

  1. 有向图用 \(scc\),无向图用 \(edcc\)\(vdcc\),有时也可以两种图互相转换
  2. 缩点后会形成一颗树或 \(DAG\),可以从上面找性质,求答案
  3. 注意 \(scc\)\(vdcc\)\(edcc\) 里面的性质
  4. 一些题目可以图论建模

题目

基环树

补充

  • 基环树找环:
    \((1)\) 无向图通过一个动态数组,只有 \(v\) 为该动态数组的最后一位的值时才把 \(u\) 加进去(还需特判 \(u\) 为入环点的情况)
    \((2)\) 有向图中,内向图用 \(topo\),外向图转为内向图

  • 基环树的处理方法:
    \((1)\)在环上找性质
    \((2)\)断开环
    \((3)\)缩点

题目

生成树

最小生成树 (MST)

常见用法

OI-Wiki 最小生成树

补充

  • 写代码时注意区分 \(n,m\)

题目

dfs 生成树

补充

  • \(dfs\) 生成树性质:
    1.如果是无向图,只有树边,返祖边
    2.如果是有向图,有树边,返祖边,横叉边,前向边

题目

  • P11954 「ZHQOI R1」删边
    简要思路:构造题,在图上比较难做,可以考虑 \(dfs\) 生成树,则可以删去所有返祖边,形成一棵树,再删去树上的一条边即可,注意细节(菊花图要特殊考虑),具体看题解

第四板块——数据结构

线段树

线段树基础

常见用法

OI-Wiki 线段树基础

题目

  • P4588 [TJOI2018] 数学计算
    简要思路:由于不保证模数是质数,所以用逆元做不行,此时我们可以发现两个操作都可以转换为单点修改,记录第 \(i\) 次询问乘的树,按题意单点修改,查询就输出 \(tr[1]\),即可

  • ABC397 F - Variety Split Hard
    思路:
    考虑先把数列分成两份,枚举分的位置,对于后半部分直接计算贡献,对于前半部分需要再切分一次,尝试在 \(O(log \times n)\) 的时间内计算贡献。
    容易发现可以预处理出 \(fcnt_i\) 表示 \(1\)\(i\) 中切分一次的贡献,注意到,从 \(i-1\) 转移到 \(i\) 的过程中,相当于增加一个断点,所以可以维护一个数列 \(B_i\) 表示断在第 \(i\) 个位置的贡献,需要区间修改和查询最值。
    具体地说,记 \(lst_x\) 表示 \(x\) 这个数上一次出现的位置,则从 \(i-1\) 转移到 \(i\) 的过程中,从 \(lst_{A_i}\)\(i-1\) 的位置要加上 \(1\),同时还要新增一个断点,记录它的贡献,具体实现见代码

  • ABC407 F - Sums of Sliding Window Maximum
    思路:见我的题解

  • P4513 小白逛公园
    简要思路:具体看题解

线段树合并

常见用法

OI-Wiki 线段树合并

补充

  • 注意线段树合并递归到某个节点时,如果 \(A\) 树或者 \(B\) 树上的对应节点为空,便直接返回另一个树上对应节点。如果下一次再次合并的话,就有可能修改这个节点的值,而这个节点可能本来是另一棵已经更新完的线段树。
    则线段树合并时可能会修改别的已更新完线段树的值,所以我们需要在更新完第 \(i\) 线段树时及时记录 \(i\) 的答案

题目

P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
简要思路:树上差分转化为单点修改,直接更新线段树,最后 \(dfs\) 合并即可

P3605 [USACO17JAN] Promotion Counting P
简要思路:本题有非常多做法:

  1. 树状数组。我们可以 \(dfs\) 记录答案,这里有个比较套路的方法:我们可以用全局权值树状数组维护,观察到,\(dfs\)\(u\) 时,前面可能有一些点的贡献会被计算,记为 \(s\),则我们可以先 \(dfs\) 完子树,再查询,贡献就为 \(s+ans_u\),所以我们便可这样计算出答案
  2. 主席树。查询子树可以通过 \(dfs\) 序转化为区间查询,然后就变成主席树板子
  3. 线段树合并。由于只有 \(n\) 个点,所以可以动态开点线段树,每次向上合并再查询

扫描线

常见用法

OI-Wiki 扫描线

题目

  • P5490 【模板】扫描线 & 矩形面积并
    简要思路:板子题

  • P1856 [IOI 1998 ] [USACO5.5] 矩形周长Picture
    简要思路:跟矩阵面积并有些像,按 \(x\)\(y\) 分别扫描一次,扫描时横线的贡献等于 \(abs\) (当前总区间覆盖长度 - 上一次总区间覆盖长度),因为每添加一条边,如果没有使总区间覆盖长度发生变化,说明这条边在矩形内部,被包含了,不用计算;如果引起总区间长度发生变化,说明这条边不被包含,应该计算。此外,如果两条扫描线的 \(y\) 相等,那么需要先计算入边,原因显然

第六板块——动态规划

动态规划基础

常见用法

OI-Wiki 动态规划基础

线性DP

题目

P12406 「CZOI-R3」消除序列
思路:
首先发现,交换次数可以归纳为奇数和偶数两种情况,于是定义 \(f_{i,0/1}\) 表示消到 \(B\) 的第 \(i\) 个数时,\(x,y\) 的交换次数为偶数或奇数的答案
\(i\) 次循环时,记 \(w_0\) 表示交换次数为偶数的最小代价,\(w_1\) 表示交换次数为奇数的最小代价,则有转移方程:

\[f_{i,0} = \min(f_{i-1,0}+w_0,f_{i-1,1}+w_1+z) \]

\[f_{i,1} = \min(f_{i-1,1}+w_1,f_{i-1,0}+w_0+z) \]

现在瓶颈在于计算 \(w_0\)\(w_1\)

首先,我们可以计算 \(A_i\) 在第 \(i-1\) 次操作的位置,记为 \(lpos\),然后我们就需要把 \(A_i\)\(lpos\) 移到 \(1\),但这中间可能有数为 \(0\) 导致有部分贡献无需计算,容易发现可以树状数组维护,这里可以用化环为链的方法,但还是需要注意一些边界情况

树形DP

常见用法

OI-Wiki 树形 DP

补充

  • 树形 DP 的状态多样,先考虑经典状态,实在做不了再考虑其他状态,转移方程需仔细推敲

题目

P12734 理解
思路:本题我们可以先定义一个直接出答案的状态:\(f_{u,i}\) 表示以 \(u\) 为根的子树内记忆容量为 \(i\) 的最少时间,特别地,\(f_{u,0}\) 表是以 \(u\) 为根的子树内,不选 \(u\) 的最少时间。则答案应为 \(f_{0,0}\),所以 \(f_u\) 应该不记录 \(u\) 的贡献。
对于状态转移,首先有:

\[f_{u,1} = \sum_{v \in son(u)} \min(f_{v,0},f_{v,k}+r_v) \]

\[f_{u,0} = \begin{cases} \infty & u \in x \\ f_{u,1} & u \notin x \\ \end{cases}\]

考虑 \(i \in [2,k]\),有三种转移情形:

  1. \(v\) 不选,此时用于转移的是 \(f_{v,0}\)
  2. \(v\) 作为根,此时用于转移的是 \(f_{v,k}+r_v\)
  3. \(v\) 作为 \(u\) 的儿子,此时用于转移的是 \(f_{v,i-1}+t_u\)

但是,我们发现,对于 \(u\) 子树和 \(i\) 的记忆容量,\(u\) 的儿子中可以有最多一个记忆容量为 \(k\),其余都为 \(k-1\)
这时,我们可以按以下方式处理,就是先算出和再减去 \(v\) 的原贡献加上新贡献

F(i,2,k){
		ll s = 0;
		for (auto v:go[u]){
			s += min(dp[v][0],min(dp[v][k]+ar[v],dp[v][i-1]+br[v]));
		}
		dp[u][i] = s;
		for (auto v:go[u]){
			dp[u][i] = min(dp[u][i],s-min(dp[v][0],min(dp[v][k]+ar[v],dp[v][i-1]+br[v]))+min(dp[v][0],min(dp[v][k]+ar[v],dp[v][i]+br[v])));
		}
	}

最后注意一下多测清空

第十板块——常见技巧

离线处理

简述:把询问离线排序,按一定顺序处理

题目

  • P10814 【模板】离线二维数点
    简要思路:对询问按 \(k\) 值排序,然后遍历询问,可以直接 \(1∼n\)\(\le que[i].k\) 的点,现在就是要维护 \([l,r]\) 中存在多少个元素 \(\le x\),权值树状数组可以做到,故时间复杂度 \(O(n\times \log n)\)
posted @ 2025-07-19 09:18  huangyuze  阅读(38)  评论(0)    收藏  举报