牛客网笔试模板必刷题 - 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)