牛客网笔试模板必刷题 - Python算法学习指南

题单链接: 牛客网笔试模板必刷题
题目总数: 147道
题单定位: 笔试TOP101的升级版,全部采用ACM模式,适合即将参加笔试、需要快速补全笔试算法知识点的同学学习
覆盖范围: 覆盖了90%+的笔面试算法知识点
目录
- 题单概览
- 基础算法
- 贪心算法
- 数学算法
- 搜索算法
- 树图算法
- 数据结构
- 动态规划
一、题单概览
1.1 分类结构统计
| 大类 | 子分类 | 题目数量 | 核心知识点 |
|---|
| 基础算法 | 简单库函数数据结构 | 8 | vector、stack、queue、set、multiset、priority_queue |
| 基础算法 | 暴力枚举 | 5 | 枚举、贪心策略 |
| 基础算法 | 模拟 | 7 | 日期计算、字符串处理、模拟过程 |
| 基础算法 | 排序 | 3 | 快速排序、桶排序 |
| 基础算法 | 桶思想 | 2 | 桶排序、离散化 |
| 基础算法 | 构造 | 4 | 构造算法、数学构造 |
| 基础算法 | 位运算 | 4 | 位运算、异或运算 |
| 基础算法 | 博弈论 | 3 | 巴什博弈、扩展巴什博弈 |
| 基础算法 | 随机化 | 4 | 随机化算法、Pollard-Rho |
| 基础算法 | 整除分块 | 2 | 整除分块、数论分块 |
| 贪心算法 | 简单贪心 | 7 | 贪心策略、排序贪心 |
| 贪心算法 | 贪心进阶 | 5 | 区间贪心、排序不等式 |
| 数学算法 | 简单数论 | 4 | 质数判断、质因数分解、GCD/LCM |
| 数学算法 | 模运算/快速幂 | 8 | 快速幂、矩阵快速幂、模运算 |
| 数学算法 | 组合数学 | 6 | 组合数、排列组合、容斥原理 |
| 数学算法 | 高级数论 | 3 | 欧拉函数、乘法逆元、欧拉降幂 |
| 搜索算法 | DFS | 4 | 深度优先搜索、回溯 |
| 搜索算法 | BFS | 4 | 广度优先搜索、最短路径 |
| 搜索算法 | 二分 | 5 | 整数二分、实数二分 |
| 搜索算法 | 记忆化搜索 | 2 | 记忆化、剪枝 |
| 搜索算法 | 字符串匹配 | 3 | KMP、Trie树、马拉车 |
| 树图算法 | 树图基础 | 7 | 链式前向星、遍历、图论基础 |
| 树图算法 | 并查集 | 3 | 并查集、路径压缩 |
| 树图算法 | 最短路/生成树 | 5 | Dijkstra、BFS、Kruskal/Prim |
| 数据结构 | 前缀和与差分 | 4 | 一维/二维前缀和、差分 |
| 数据结构 | 双指针 | 7 | 滑动窗口、双指针技巧 |
| 数据结构 | 单调栈/队列 | 2 | 单调栈、单调队列 |
| 数据结构 | 倍增/LCA/ST表 | 3 | LCA、RMQ、倍增 |
| 数据结构 | 高级数据结构 | 5 | 线段树、树状数组 |
| 动态规划 | 简单DP | 6 | 线性DP、背包基础 |
| 动态规划 | 进阶DP | 12 | 树形DP、区间DP、状态压缩DP |
1.2 难度分布
| 难度级别 | 题目数量 | 占比 |
|---|
| 入门 | 6道 | 4.1% |
| 简单 | 56道 | 38.1% |
| 中等 | 67道 | 45.6% |
| 较难 | 16道 | 10.9% |
| 困难 | 2道 | 1.4% |
二、基础算法
2.1 简单库函数数据结构(8道)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI1 | 【模板】序列操作 | 简单 | vector操作 |
| BISHI2 | 【模板】栈的操作 | 简单 | stack操作 |
| BISHI3 | 【模板】队列操作 | 简单 | queue操作 |
| BISHI4 | 【模板】集合操作 | 简单 | set操作 |
| BISHI5 | 【模板】多重集合操作 | 简单 | multiset操作 |
| BISHI6 | 【模板】整数优先队列 | 简单 | priority_queue |
| BISHI7 | 字符串哈希 | 简单 | 字符串哈希 |
| BISHI8 | 大整数哈希 | 简单 | 大数哈希 |
2.2 暴力枚举(5道)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI9 | 田忌赛马 | 中等 | 贪心+枚举 |
| BISHI10 | 小红的字符串修改 | 简单 | 字符串枚举 |
| BISHI11 | 变幻莫测 | 简单 | 模拟枚举 |
| BISHI12 | 元素方碑 | 中等 | 枚举+模拟 |
| BISHI13 | 九倍平方数 | 简单 | 数学枚举 |
2.3 模拟(7道)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI14 | 特殊的科学计数法 | 简单 | 字符串模拟 |
| BISHI15 | 小红的夹吃棋 | 简单 | 博弈模拟 |
| BISHI16 | 计算一年中的第几天 | 入门 | 日期模拟 |
| BISHI17 | 纸牌游戏 | 简单 | 游戏模拟 |
| BISHI18 | 多项式输出 | 简单 | 格式模拟 |
| BISHI19 | 乒乓球 | 简单 | 规则模拟 |
| BISHI20 | 回文日期 | 简单 | 日期判断 |
2.4 排序(3道)
def quick_sort(nums, left, right):
if left >= right:
return
pivot = nums[left]
i, j = left, right
while i < j:
while i < j and nums[j] >= pivot:
j -= 1
while i < j and nums[i] <= pivot:
i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[left], nums[i] = nums[i], nums[left]
quick_sort(nums, left, i - 1)
quick_sort(nums, i + 1, right)
return nums
def merge_sort(nums):
if len(nums) <= 1:
return nums
mid = len(nums) // 2
left = merge_sort(nums[:mid])
right = merge_sort(nums[mid:])
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI21 | 【模板】排序 | 入门 | 快速排序 |
| BISHI22 | 分数线划定 | 简单 | 排序+贪心 |
| BISHI23 | 小红书推荐系统 | 简单 | 排序应用 |
2.5 位运算(4道)
is_odd = x & 1
lowbit = x & (-x)
x = x & (x - 1)
is_power_of_2 = x > 0 and (x & (x - 1)) == 0
a = a ^ b
b = a ^ b
a = a ^ b
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI30 | 二进制数1 | 简单 | 位运算 |
| BISHI31 | 二进制不同位数 | 简单 | 位运算 |
| BISHI32 | 被打乱的异或和 | 简单 | 异或运算 |
| BISHI33 | Poi的新加法(Easy) | 简单 | 位运算模拟 |
2.6 博弈论(3道)
def bash_game(n, m):
return n % (m + 1) != 0
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI34 | 甜蜜的博弈 | 简单 | 博弈 |
| BISHI35 | 【模板】巴什博弈 | 中等 | 巴什博弈 |
| BISHI36 | 【模板】扩展巴什博弈 | 较难 | 扩展巴什博弈 |
三、贪心算法
3.1 简单贪心(7道)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI43 | 讨厌鬼进货 | 入门 | 贪心 |
| BISHI44 | 灵异背包? | 简单 | 贪心背包 |
| BISHI45 | 小红的矩阵染色 | 简单 | 贪心染色 |
| BISHI46 | 小红的魔法药剂 | 简单 | 贪心 |
| BISHI47 | 交换到最大 | 简单 | 贪心交换 |
| BISHI48 | 小红的整数配对 | 简单 | 贪心配对 |
| BISHI49 | 小红闯关 | 中等 | 贪心策略 |
3.2 贪心进阶(5道)
def interval_scheduling(intervals):
intervals.sort(key=lambda x: x[1])
count = 1
end = intervals[0][1]
for i in range(1, len(intervals)):
if intervals[i][0] >= end:
count += 1
end = intervals[i][1]
return count
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI50 | [JSOI2007]建筑抢修 | 中等 | 区间贪心 |
| BISHI51 | 低买高卖 | 中等 | 贪心交易 |
| BISHI52 | 奥赛组队 | 中等 | 排序贪心 |
| BISHI53 | [P1080]国王游戏(简化版) | 中等 | 排序不等式 |
| BISHI54 | 货物堆放 | 中等 | 贪心 |
四、数学算法
4.1 简单数论(4道)
import math
def is_prime(n):
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
def prime_factors(n):
factors = []
i = 2
while i * i <= n:
while n % i == 0:
factors.append(i)
n //= i
i += 1
if n > 1:
factors.append(n)
return factors
def gcd(a, b):
return math.gcd(a, b)
def lcm(a, b):
return a * b // gcd(a, b)
def exgcd(a, b):
if b == 0:
return a, 1, 0
g, x1, y1 = exgcd(b, a % b)
x = y1
y = x1 - (a // b) * y1
return g, x, y
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI55 | 判断质数 | 入门 | 质数判断 |
| BISHI56 | 分解质因数 | 简单 | 质因数分解 |
| BISHI57 | 最大公因数与最小公倍数 | 简单 | GCD/LCM |
| BISHI58 | 矩形游戏 | 简单 | 数学 |
4.2 模运算与快速幂(8道)
def fast_pow(base, exp, mod):
result = 1
base = base % mod
while exp > 0:
if exp & 1:
result = result * base % mod
base = base * base % mod
exp >>= 1
return result
def matrix_mult(A, B, mod):
n = len(A)
m = len(B[0])
p = len(B)
C = [[0] * m for _ in range(n)]
for i in range(n):
for j in range(m):
for k in range(p):
C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod
return C
def matrix_pow(M, n, mod):
if n == 1:
return M
if n % 2 == 0:
half = matrix_pow(M, n // 2, mod)
return matrix_mult(half, half, mod)
else:
return matrix_mult(M, matrix_pow(M, n - 1, mod), mod)
def mod_inverse(a, mod):
return fast_pow(a, mod - 2, mod)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI59 | 阶乘末尾非零数字 | 中等 | 数论 |
| BISHI60 | 大水题 | 简单 | 快速幂 |
| BISHI61 | 小q的数列 | 简单 | 递推 |
| BISHI62 | 斐波那契数列 | 简单 | 矩阵快速幂 |
| BISHI63 | 计算阶乘 | 简单 | 高精度 |
| BISHI64 | 【模板】快速幂Ⅰ‖整数 | 中等 | 快速幂 |
| BISHI65 | 【模板】分数取模 | 中等 | 逆元 |
| BISHI66 | 子数列求积 | 中等 | 前缀积 |
4.3 组合数学(6道)
MOD = 10**9 + 7
def precompute_factorials(n, mod):
fact = [1] * (n + 1)
inv_fact = [1] * (n + 1)
for i in range(2, n + 1):
fact[i] = fact[i-1] * i % mod
inv_fact[n] = pow(fact[n], mod - 2, mod)
for i in range(n-1, -1, -1):
inv_fact[i] = inv_fact[i+1] * (i+1) % mod
return fact, inv_fact
def comb(n, k, fact, inv_fact, mod):
if k < 0 or k > n:
return 0
return fact[n] * inv_fact[k] % mod * inv_fact[n-k] % mod
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI67 | 穿搭大挑战 | 简单 | 组合 |
| BISHI68 | 刷题统计 | 简单 | 数学 |
| BISHI69 | [HNOI2008]越狱 | 简单 | 容斥 |
| BISHI70 | 【模板】组合数 | 中等 | 组合数 |
| BISHI71 | 人员分组问题 | 中等 | 组合 |
| BISHI72 | 中位数之和 | 较难 | 数学 |
4.4 高级数论(3道)
def euler_phi(n):
result = n
p = 2
while p * p <= n:
if n % p == 0:
while n % p == 0:
n //= p
result -= result // p
p += 1
if n > 1:
result -= result // n
return result
def euler_pow(a, b, mod):
phi = euler_phi(mod)
if b >= phi:
b = b % phi + phi
return pow(a, b, mod)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI73 | 【模板】欧拉函数Ⅰ‖单个整数 | 中等 | 欧拉函数 |
| BISHI74 | 【模板】非质模数下的乘法逆元 | 中等 | 逆元 |
| BISHI75 | 【模板】欧拉降幂 | 中等 | 欧拉降幂 |
五、搜索算法
5.1 DFS(4道)
def dfs(node, visited):
if node in visited:
return
visited.add(node)
for neighbor in graph[node]:
dfs(neighbor, visited)
def permute(nums):
result = []
n = len(nums)
def backtrack(path, used):
if len(path) == n:
result.append(path[:])
return
for i in range(n):
if not used[i]:
used[i] = True
path.append(nums[i])
backtrack(path, used)
path.pop()
used[i] = False
backtrack([], [False] * n)
return result
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI76 | 迷宫寻路 | 简单 | DFS |
| BISHI77 | 数水坑 | 简单 | Flood Fill |
| BISHI78 | 全排列 | 简单 | 回溯 |
| BISHI79 | 取数游戏 | 中等 | DFS+剪枝 |
5.2 BFS(4道)
from collections import deque
def bfs(start, target):
queue = deque([start])
visited = {start}
distance = {start: 0}
while queue:
node = queue.popleft()
if node == target:
return distance[node]
for neighbor in get_neighbors(node):
if neighbor not in visited:
visited.add(neighbor)
distance[neighbor] = distance[node] + 1
queue.append(neighbor)
return -1
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI80 | 走迷宫 | 简单 | BFS |
| BISHI81 | 剪纸游戏 | 简单 | BFS |
| BISHI82 | 没挡住洪水 | 简单 | BFS |
| BISHI83 | 迷宫问题 | 中等 | BFS最短路 |
5.3 二分(5道)
def binary_search_left(nums, target):
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid
return left
def binary_search_right(nums, target):
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right + 1) // 2
if nums[mid] > target:
right = mid - 1
else:
left = mid
return left
def check(mid):
pass
def binary_search_answer(left, right):
while left < right:
mid = (left + right) // 2
if check(mid):
right = mid
else:
left = mid + 1
return left
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI85 | 【模板】整数域二分 | 简单 | 二分查找 |
| BISHI86 | 圆覆盖 | 中等 | 二分答案 |
| BISHI87 | [CQOI2010]扑克牌 | 中等 | 二分 |
| BISHI88 | 小苯的魔法染色 | 中等 | 二分答案 |
| BISHI89 | 山峰数组计数 | 中等 | 二分 |
5.4 字符串匹配算法(3道)
def build_lps(pattern):
lps = [0] * len(pattern)
length = 0
i = 1
while i < len(pattern):
if pattern[i] == pattern[length]:
length += 1
lps[i] = length
i += 1
else:
if length != 0:
length = lps[length - 1]
else:
lps[i] = 0
i += 1
return lps
def kmp_search(text, pattern):
if not pattern:
return 0
lps = build_lps(pattern)
i = j = 0
while i < len(text):
if text[i] == pattern[j]:
i += 1
j += 1
if j == len(pattern):
return i - j
else:
if j != 0:
j = lps[j - 1]
else:
i += 1
return -1
class TrieNode:
def __init__(self):
self.children = {}
self.is_end = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_end = True
def search(self, word):
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_end
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI92 | 【模板】前缀函数(KMP) | 中等 | KMP |
| BISHI93 | 【模板】Trie字典树 | 中等 | Trie |
| BISHI94 | 【模板】马拉车算法 | 中等 | Manacher |
六、树图算法
6.1 树图基础(7道)
class Graph:
def __init__(self, n):
self.n = n
self.head = [-1] * n
self.to = []
self.nxt = []
self.cnt = 0
def add_edge(self, u, v):
self.to.append(v)
self.nxt.append(self.head[u])
self.head[u] = self.cnt
self.cnt += 1
def tree_dfs(root, graph, visited):
visited.add(root)
for child in graph[root]:
if child not in visited:
tree_dfs(child, graph, visited)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI95 | 【模板】链式前向星 | 简单 | 图存储 |
| BISHI96 | 先序中序后序遍历 | 简单 | 树遍历 |
| BISHI97 | 旺仔哥哥走迷宫 | 中等 | 图论 |
| BISHI98 | 谍中谍中谍… | 中等 | 图论 |
| BISHI99 | 我朋友的朋友不是我的朋友 | 中等 | 图论 |
| BISHI100 | 二分图判定 | 中等 | 二分图 |
| BISHI101 | 世界树上找米库 | 中等 | 树形DP |
6.2 并查集(3道)
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
px, py = self.find(x), self.find(y)
if px == py:
return False
if self.rank[px] < self.rank[py]:
px, py = py, px
self.parent[py] = px
if self.rank[px] == self.rank[py]:
self.rank[px] += 1
return True
def is_connected(self, x, y):
return self.find(x) == self.find(y)
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI102 | 【模板】并查集 | 较难 | 并查集 |
| BISHI103 | 【模板】有依赖的背包问题 | 中等 | 树形DP |
| BISHI104 | 修复公路 | 中等 | 并查集+Kruskal |
6.3 最短路/生成树(5道)
import heapq
def dijkstra(graph, start, n):
dist = [float('inf')] * n
dist[start] = 0
pq = [(0, start)]
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]:
continue
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
heapq.heappush(pq, (dist[v], v))
return dist
def kruskal(edges, n):
edges.sort(key=lambda x: x[2])
uf = UnionFind(n)
mst_weight = 0
for u, v, w in edges:
if uf.union(u, v):
mst_weight += w
return mst_weight
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI105 | 【模板】单源最短路Ⅰ‖无权图 | 中等 | BFS |
| BISHI106 | 【模板】单源最短路Ⅲ‖非负权图 | 较难 | Dijkstra |
| BISHI107 | 【模板】最小生成树 | 较难 | Kruskal/Prim |
| BISHI108 | 最优乘车 | 简单 | 最短路 |
| BISHI109 | 邮递员送信 | 中等 | 最短路 |
七、数据结构
7.1 前缀和与差分(4道)
class PrefixSum:
def __init__(self, nums):
self.prefix = [0]
for num in nums:
self.prefix.append(self.prefix[-1] + num)
def range_sum(self, left, right):
return self.prefix[right + 1] - self.prefix[left]
def build_2d_prefix(matrix):
m, n = len(matrix), len(matrix[0])
prefix = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m):
for j in range(n):
prefix[i+1][j+1] = prefix[i][j+1] + prefix[i+1][j] - prefix[i][j] + matrix[i][j]
return prefix
def query_2d_prefix(prefix, r1, c1, r2, c2):
return prefix[r2+1][c2+1] - prefix[r1][c2+1] - prefix[r2+1][c1] + prefix[r1][c1]
class Difference:
def __init__(self, n):
self.diff = [0] * (n + 2)
def add(self, left, right, val):
self.diff[left] += val
self.diff[right + 1] -= val
def get_result(self):
result = []
cur = 0
for i in range(len(self.diff) - 1):
cur += self.diff[i]
result.append(cur)
return result
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI110 | 【模板】静态区间和(前缀和) | 简单 | 前缀和 |
| BISHI111 | 【模板】差分 | 简单 | 差分 |
| BISHI112 | 【模板】二维前缀和 | 中等 | 二维前缀和 |
| BISHI113 | 【模板】二维差分 | 中等 | 二维差分 |
7.2 双指针(7道)
from collections import defaultdict
def sliding_window(s, t):
need = defaultdict(int)
for char in t:
need[char] += 1
required = len(t)
left = 0
min_len = float('inf')
min_start = 0
for right, char in enumerate(s):
if need[char] > 0:
required -= 1
need[char] -= 1
while required == 0:
if right - left + 1 < min_len:
min_len = right - left + 1
min_start = left
need[s[left]] += 1
if need[s[left]] > 0:
required += 1
left += 1
return "" if min_len == float('inf') else s[min_start:min_start + min_len]
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI114 | 【模板】滑动窗口 | 简单 | 滑动窗口 |
| BISHI115 | 可匹配子段计数 | 简单 | 双指针 |
| BISHI116 | 【模板】双指针 | 中等 | 双指针 |
| BISHI117 | 小苯的IDE括号问题(easy) | 中等 | 双指针 |
| BISHI118 | 相差不超过k的最多数 | 中等 | 滑动窗口 |
| BISHI119 | 小红的01子序列构造(easy) | 中等 | 双指针 |
| BISHI120 | ??? | 较难 | 双指针 |
7.3 单调栈和单调队列(2道)
def next_greater_elements(nums):
n = len(nums)
result = [-1] * n
stack = []
for i in range(n):
while stack and nums[stack[-1]] < nums[i]:
result[stack.pop()] = nums[i]
stack.append(i)
return result
from collections import deque
def max_sliding_window(nums, k):
result = []
dq = deque()
for i, num in enumerate(nums):
while dq and dq[0] <= i - k:
dq.popleft()
while dq and nums[dq[-1]] < num:
dq.pop()
dq.append(i)
if i >= k - 1:
result.append(nums[dq[0]])
return result
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI121 | 数列后缀极大位置统计 | 简单 | 单调栈 |
| BISHI122 | 区间后缀极大位置计数 | 简单 | 单调栈 |
7.4 倍增/LCA与ST表(3道)
class LCA:
def __init__(self, n, graph):
self.n = n
self.graph = graph
self.LOG = 20
self.parent = [[-1] * n for _ in range(self.LOG)]
self.depth = [0] * n
self.dfs(0, -1)
self.build()
def dfs(self, u, p):
self.parent[0][u] = p
for v in self.graph[u]:
if v != p:
self.depth[v] = self.depth[u] + 1
self.dfs(v, u)
def build(self):
for k in range(1, self.LOG):
for v in range(self.n):
if self.parent[k-1][v] != -1:
self.parent[k][v] = self.parent[k-1][self.parent[k-1][v]]
def query(self, u, v):
if self.depth[u] < self.depth[v]:
u, v = v, u
for k in range(self.LOG - 1, -1, -1):
if self.depth[u] - (1 << k) >= self.depth[v]:
u = self.parent[k][u]
if u == v:
return u
for k in range(self.LOG - 1, -1, -1):
if self.parent[k][u] != self.parent[k][v]:
u = self.parent[k][u]
v = self.parent[k][v]
return self.parent[0][u]
class SparseTable:
def __init__(self, arr):
n = len(arr)
self.LOG = (n).bit_length()
self.st = [[0] * n for _ in range(self.LOG)]
self.st[0] = arr[:]
for k in range(1, self.LOG):
for i in range(n - (1 << k) + 1):
self.st[k][i] = max(self.st[k-1][i], self.st[k-1][i + (1 << (k-1))])
def query(self, l, r):
k = (r - l + 1).bit_length() - 1
return max(self.st[k][l], self.st[k][r - (1 << k) + 1])
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI123 | 环形字符串跃迁 | 中等 | 倍增 |
| BISHI124 | 【模板】最近公共祖先(LCA) | 中等 | LCA |
| BISHI125 | 【模板】静态区间最值 | 中等 | ST表 |
7.5 高级数据结构(5道)
class BIT:
def __init__(self, n):
self.n = n
self.tree = [0] * (n + 1)
def lowbit(self, x):
return x & (-x)
def update(self, i, delta):
while i <= self.n:
self.tree[i] += delta
i += self.lowbit(i)
def query(self, i):
res = 0
while i > 0:
res += self.tree[i]
i -= self.lowbit(i)
return res
def range_query(self, l, r):
return self.query(r) - self.query(l - 1)
class SegmentTree:
def __init__(self, arr):
self.n = len(arr)
self.tree = [0] * (4 * self.n)
self.lazy = [0] * (4 * self.n)
self.build(arr, 1, 0, self.n - 1)
def build(self, arr, node, start, end):
if start == end:
self.tree[node] = arr[start]
return
mid = (start + end) // 2
self.build(arr, 2*node, start, mid)
self.build(arr, 2*node+1, mid+1, end)
self.tree[node] = self.tree[2*node] + self.tree[2*node+1]
def push_down(self, node, start, end):
if self.lazy[node] != 0:
mid = (start + end) // 2
self.tree[2*node] += self.lazy[node] * (mid - start + 1)
self.tree[2*node+1] += self.lazy[node] * (end - mid)
self.lazy[2*node] += self.lazy[node]
self.lazy[2*node+1] += self.lazy[node]
self.lazy[node] = 0
def update(self, node, start, end, l, r, val):
if l <= start and end <= r:
self.tree[node] += val * (end - start + 1)
self.lazy[node] += val
return
self.push_down(node, start, end)
mid = (start + end) // 2
if l <= mid:
self.update(2*node, start, mid, l, r, val)
if r > mid:
self.update(2*node+1, mid+1, end, l, r, val)
self.tree[node] = self.tree[2*node] + self.tree[2*node+1]
def query(self, node, start, end, l, r):
if l <= start and end <= r:
return self.tree[node]
self.push_down(node, start, end)
mid = (start + end) // 2
res = 0
if l <= mid:
res += self.query(2*node, start, mid, l, r)
if r > mid:
res += self.query(2*node+1, mid+1, end, l, r)
return res
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI126 | 【模板】动态区间和Ⅱ‖区间修改+查询 | 较难 | 线段树 |
| BISHI127 | 区间根号与区间求和 | 中等 | 线段树 |
| BISHI128 | 区间加乘与单点求值 | 中等 | 线段树 |
| BISHI129 | 区间增量与区间小于计数 | 中等 | 树状数组 |
| BISHI130 | 区间取反与区间数一 | 中等 | 线段树 |
八、动态规划
8.1 简单动态规划(6道)
class Solution:
def climbStairs(self, n):
if n <= 2:
return n
prev2, prev1 = 1, 2
for i in range(3, n + 1):
current = prev1 + prev2
prev2, prev1 = prev1, current
return prev1
def knapsack_01(weights, values, capacity):
n = len(weights)
dp = [0] * (capacity + 1)
for i in range(n):
for j in range(capacity, weights[i] - 1, -1):
dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
return dp[capacity]
def knapsack_complete(weights, values, capacity):
n = len(weights)
dp = [0] * (capacity + 1)
for i in range(n):
for j in range(weights[i], capacity + 1):
dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
return dp[capacity]
def length_of_lis(nums):
if not nums:
return 0
dp = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
def max_subarray_sum(nums):
dp = [0] * len(nums)
dp[0] = nums[0]
max_sum = nums[0]
for i in range(1, len(nums)):
dp[i] = max(nums[i], dp[i-1] + nums[i])
max_sum = max(max_sum, dp[i])
return max_sum
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI131 | 数楼梯 | 简单 | 递推 |
| BISHI132 | 小红的地砖 | 简单 | 线性DP |
| BISHI133 | 最长不下降子序列 | 简单 | LIS |
| BISHI134 | 最大子段和 | 简单 | 线性DP |
| BISHI135 | 三角形取数(Hard) | 中等 | 线性DP |
| BISHI136 | 【模板】01背包 | 简单 | 背包DP |
8.2 进阶动态规划(12道)
def knapsack_complete(weights, values, capacity):
n = len(weights)
dp = [0] * (capacity + 1)
for i in range(n):
for j in range(weights[i], capacity + 1):
dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
return dp[capacity]
def knapsack_multiple(weights, values, counts, capacity):
n = len(weights)
dp = [0] * (capacity + 1)
for i in range(n):
k = 1
while counts[i] > 0:
use = min(k, counts[i])
w, v = weights[i] * use, values[i] * use
for j in range(capacity, w - 1, -1):
dp[j] = max(dp[j], dp[j - w] + v)
counts[i] -= use
k *= 2
return dp[capacity]
def tree_dp(graph, values, root=0):
n = len(graph)
dp = [[0, 0] for _ in range(n)]
def dfs(u, parent):
dp[u][1] = values[u]
for v in graph[u]:
if v != parent:
dfs(v, u)
dp[u][0] += max(dp[v][0], dp[v][1])
dp[u][1] += dp[v][0]
dfs(root, -1)
return max(dp[root])
def stone_merge(stones):
n = len(stones)
prefix = [0] * (n + 1)
for i in range(n):
prefix[i+1] = prefix[i] + stones[i]
dp = [[0] * n for _ in range(n)]
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
dp[i][j] = float('inf')
for k in range(i, j):
cost = dp[i][k] + dp[k+1][j] + prefix[j+1] - prefix[i]
dp[i][j] = min(dp[i][j], cost)
return dp[0][n-1]
def tsp(dist):
n = len(dist)
dp = [[float('inf')] * n for _ in range(1 << n)]
dp[1][0] = 0
for mask in range(1 << n):
for u in range(n):
if not (mask & (1 << u)):
continue
for v in range(n):
if mask & (1 << v):
continue
new_mask = mask | (1 << v)
dp[new_mask][v] = min(dp[new_mask][v], dp[mask][u] + dist[u][v])
return min(dp[(1 << n) - 1][i] + dist[i][0] for i in range(n))
| 题号 | 题目名称 | 难度 | 核心考察知识点 |
|---|
| BISHI137 | 【模板】完全背包 | 中等 | 完全背包 |
| BISHI138 | 【模板】多重背包 | 中等 | 多重背包 |
| BISHI139 | 【模板】二维费用背包 | 中等 | 二维背包 |
| BISHI140 | 【模板】分组背包 | 较难 | 分组背包 |
| BISHI141 | 来硬的 | 较难 | 背包DP |
| BISHI142 | 最大学分 | 较难 | 背包DP |
| BISHI143 | 没有上司的舞会 | 较难 | 树形DP |
| BISHI144 | 食物链计数 | 较难 | 树形DP |
| BISHI145 | 石子合并 | 较难 | 区间DP |
| BISHI146 | 收集金币 | 中等 | 状态压缩DP |
| BISHI147 | 旅行者的大逃脱 | 困难 | 综合DP |
附录:ACM模式输入输出模板
import sys
input = sys.stdin.readline
n = int(input())
a, b, c = map(int, input().split())
arr = list(map(int, input().split()))
n = int(input())
matrix = []
for _ in range(n):
row = list(map(int, input().split()))
matrix.append(row)
for line in sys.stdin:
a, b = map(int, line.split())
print(' '.join(map(str, result)))
while True:
try:
n = int(input())
except:
break
附录:时间/空间复杂度速查表
| 算法类型 | 典型时间复杂度 | 空间复杂度 |
|---|
| 数组遍历 | O(n) | O(1) |
| 双指针 | O(n) | O(1) |
| 二分查找 | O(log n) | O(1) |
| 哈希查找 | O(1) | O(n) |
| 快速排序 | O(n log n)平均 | O(log n) |
| 归并排序 | O(n log n) | O(n) |
| 堆排序 | O(n log n) | O(1) |
| 二叉树遍历 | O(n) | O(h) |
| 图的BFS/DFS | O(V+E) | O(V) |
| 动态规划 | O(n)或O(n²) | O(n)或O(n²) |
| 回溯算法 | O(2ⁿ)或O(n!) | O(n) |
| Dijkstra | O((V+E)log V) | O(V) |
| 线段树操作 | O(log n) | O(n) |
| 树状数组操作 | O(log n) | O(n) |
参考资源
所有评论(0)