写在前面
一些可以直接调用的板子我都在下面给出,主要适用于蓝桥杯的Python选手。一些没有固定模板的写法在本文中不列出,如有重要知识点遗漏,欢迎大家指出。
Python常用算法模板
快读
import sys
input = sys.stdin.readline
记忆化
from functools import lru_cache
@lru_cache(maxsize=None)
def dfs(a, b):
pass
开递归深度
import sys
sys.setrecursionlimit(10**5)
日期问题
常用库函数:
from datetime import datetime # 日期时间
from datetime import date # 日期
from datetime import time # 时间
from datetime import timedelta # 时间间隔
有关日期天数的加减:
d = datetime(2024, 4, 7) # 设置时间为2024年4月7日
delta = timedelta(days=1) # 时间间隔为1天
d_new = d + delta # d_new为2024年4月8日
获取日期的年月日:
d = datetime(2024, 4, 7) # 设置时间为2024年4月7日
year = d.year() # 获取年
month = d.month() # 获取月
day = d.day() # 获取天
进制转换
十进制转成其他进制:
# 十进制转二进制
bin(3) # 0b11
# 十进制转八进制
oct(8) # 0o10
# 十进制转十六进制
hex(255) # 0xff
其他进制转十进制:
# 二进制转十进制
print(int('11', 2)) # 3
# 八进制转十进制
print(int('10', 8)) # 8
# 十六进制转十进制
print(int('ff', 16)) # 255
前缀和模板
一维前缀和:
a = [0, 1, 2, 3, 4, 5, 6, 7]
prex = [a[0]]
for i in range(1, len(a)):
prex.append(prex[-1] + a[i])
print(prex) # [0, 1, 3, 6, 10, 15, 21, 28]
二维前缀和:
n = 3 # 3*3的矩阵
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
prex = [[0] * (n + 1) for _ in range(n + 1)]
for i in range(1, n+1):
for j in range(1, n+1):
prex[i][j] = prex[i][j-1] + prex[i-1][j] - prex[i-1][j-1] + a[i][j]
print(prex)
差分模板
一维差分:
a = [0] + [1, 3, 2, 5, 5, 6, 7, 9]
diff = [0]
for i in range(1, len(a)):
diff.append(a[i] - a[i-1])
print(diff) # [0, 1, 2, -1, 3, 0, 1, 1, 2]
二维差分:
n = 3 # 3*3的矩阵
a = [[0, 0, 0, 0], [0, 1, 2, 3], [0, 4, 5, 6], [0, 7, 8, 9]]
diff = [[0] * (n+1) for _ in range(n+1)]
for i in range(1, n+1):
for j in range(1, n+1):
diff[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1]
print(diff) # [[0, 0, 0, 0], [0, 1, 1, 1], [0, 3, 0, 0], [0, 3, 0, 0]]
在(x1,y1)到(x2,y2)的区间均加d
diff[x1][y1] += d
diff[x2+1][y2+1] += d
diff[x1][y2+1] -= d
diff[x2+1][y1] -= d
离散化
from bisect import bisect_left
a = [1000, 2000, 900000, 3000, 4000]
def discrete(li):
tmp = sorted(set(a))
new_li = []
for i in range(len(a)):
new_li.append(bisect_left(tmp, a[i]) + 1)
return new_li
print(discrete(a)) # [1, 2, 5, 3, 4]
二分模板
a = [1, 2, 3, 5, 5, 5, 6, 7, 9, 10]
def bin_search_right(li, x):
l, r = -1, len(li)
while l + 1 != r:
mid = (l + r) // 2
if li[mid] <= x: l = mid
else: r = mid
return l
# 找数组中最右边的值
print(bin_search_right(a, 5)) # 5
a = [1, 2, 3, 5, 5, 5, 6, 7, 9, 10]
def bin_search_right(li, x):
l, r = -1, len(li)
while l + 1 != r:
mid = (l + r) // 2
if li[mid] >= x: r = mid
else: l = mid
return r
# 找数组中最左边的值
print(bin_search_right(a, 5)) # 3
埃氏筛模板
def get_prime():
N = 100010
flag = [0] * N
for i in range(2, N):
if flag[i] == 0:
for j in range(i+i, N, i):
flag[j] = 1
prime = []
for i in range(2, N):
if flag[i] == 0: prime.append(i)
return prime
print(get_prime())
快速幂模板
mod = 10**9+7 # 带mod的pow有快速幂功能
print(pow(2, 10, mod)) # 1024
def ksm(x, a): # return x**a
res = 1
while a:
if a&1:
res *= x
a >>= 1
x *= x
return res
print(ksm(2, 10)) # 1024
KMP算法
N = 1000005
Next = [0] * N
def getNext(p): # 求前缀数组
for i in range(1, len(p)):
j = Next[i]
while j > 0 and p[i] != p[j]:
j = Next[j]
if p[i] == p[j]: Next[i + 1] = j + 1
else: Next[i] = 0
def kmp(s, p): # KMP
j = 0
ans = 0
for i in range(0, len(s)):
while j > 0 and s[i] != p[j]:
j = Next[j]
if s[i] == p[j]:
j += 1
ans = max(ans, j)
if j == len(p): return ans
return ans
s = input()
p = input()
getNext(p)
print(kmp(s, p))
字符串hash
n, m = map(int, input().split())
a = [0] + list(input())
p = 131 # 固定写法防止出现歧义
mod = 10**9 + 7
h = [0] * (n + 1)
for i in range(1, n + 1):
h[i] = (h[i-1] * p + ord(a[i])) % mod
def query(l, r):
return (h[r] - h[l-1] * pow(p, (r-l+1), mod)) % mod
for _ in range(m):
l1, r1, l2, r2 = map(int, input().split())
if query(l1, r1) == query(l2, r2):
print('Yes')
else: print('No')
矩阵乘法
def mutiply(A, B):
n, m = len(A), len(A[0]) # n行m列
_m, k = len(B), len(B[0]) # m行k列
C = [[0] * (k) for i in range(n)]
for i in range(n):
for j in range(k):
for d in range(m):
C[i][j] += (A[i][d] * B[d][j]) % mod
return C # n行k列
gcd与lcm
from math import gcd
def lcm(a, b):
return a * b // gcd(a, b)
print(gcd(4, 16)) # 4
print(lcm(4, 16)) # 16
唯一分解定理
一个大于1的数N要么为质数,要么可以分解为有限个质数的乘积。
def f(n):
factor = []
for i in range(2, n+1):
while n % i == 0:
n //= i
factor.append(i)
if n == 1:
break
return factor
print(f(16))
结论:因子个数为其每个分解的质数的出现次数+1再相乘
求逆元
mod = 10**9 + 7 # mod必须为奇数
def inv(x):
return pow(x, mod-2, mod)
裴蜀定理
a, b为不全为0的整数,则存在x, y满足:ax + by = gcd(a, b)
并查集
def find(x):
if x == p[x]: return x
p[x] = find(p[x]) # 压缩路径
return p[x]
def merge(x, y):
xRoot = find(x)
yRoot = find(y)
p[xRoot] = yRoot
def query(x, y):
return find(x) == find(y)
树状数组
N = 10**9
tree = [0] * (N)
def lowbit(x):
return x & (-x)
def update(x, d):
while x <= N:
tree[x] += d
x += lowbit(x)
def query(x):
ans = 0
while x:
ans += tree[x]
x -= lowbit(x)
return ans
Floyd算法
n, m = map(int, input().split())
dp = [[0x3f3f3f3f] * (n + 1) for _ in range(n + 1)]
for _ in range(m):
x, y, z = map(int, input().split())
dp[x][y] = dp[y][x] = min(dp[x][y], z)
for k in range(1, n + 1):
for i in range(1, n + 1):
for j in range(1, n + 1):
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])
Dijkstra算法
from queue import PriorityQueue
import sys
sys.input = sys.stdin.readline
n, m = map(int, input().split())
inf = 1<<64
g = [[] for _ in range(n + 1)]
for _ in range(m):
u, v, w = map(int, input().split())
g[u].append([v, w])
def dijkstra(s):
global r
pq = PriorityQueue() # pq[0]表示下一个要走的最优路
vis = [0] * (n + 1) # 表示某点是否被标记为最优
r = [inf] * (n + 1) # 存的是起点到i的最短路
r[s] = 0 # 起点到起点的距离为0
pq.put([r[s], s]) # 把起点放入优先队列
while pq.qsize() > 0:
now = pq.get()
if vis[now[1]]: continue
vis[now[1]] = 1
for i in g[now[1]]:
v, w = i[0], i[1]
if vis[v]: continue
r[v] = min(r[v], r[now[1]] + w)
pq.put([r[v], v])
dijkstra(1)
for i in range(1, n + 1):
if r[i] >= inf: print(-1, end = ' ')
else: print(r[i], end = ' ')
线段树
单点修改、区间查询
def build(id, l, r):
if l == r:
tree[id] = a[l]
return
mid = (l + r) // 2
build(id * 2, l, mid)
build(id * 2 + 1, mid + 1, r)
tree[id] = tree[id * 2] + tree[id * 2 + 1]
def update(pos, v, id, l, r):
if l == r:
tree[id] += v
return
mid = (l + r) // 2
if pos <= mid: update(pos, v, id * 2, l, mid)
else: update(pos, v, id * 2 + 1, mid + 1, r)
tree[id] = tree[id * 2] + tree[id * 2 + 1]
def query(L, R, id, l, r):
if L > r or R < l: return 0
if L <= l and r <= R: return tree[id]
mid = (l + r) // 2
return query(L, R, id * 2, l, mid) + query(L, R, id * 2 + 1, mid + 1, r)
n = int(input())
a = [0] + list(map(int, input().split()))
tree = [0] * (4 * n)
build(1, 1, n)
for _ in range(int(input())):
op, x, y = map(int, input().split())
if op == 1:
update(x, y, 1, 1, n)
else:
print(query(x, y, 1, 1, n))
区间修改、区间查询
def build(id, l, r):
if l == r:
tree[id] = a[l]
return
mid = (l + r) // 2
build(id * 2, l, mid)
build(id * 2 + 1, mid + 1, r)
tree[id] = tree[id * 2] + tree[id * 2 + 1]
def to_down(id, sl, sr):
tag[id * 2] += tag[id]
tag[id * 2 + 1] += tag[id]
tree[id * 2] += tag[id] * sl
tree[id * 2 + 1] += tag[id] * sr
tag[id] = 0
def update(L, R, v, id, l, r):
if R < l or L > r: return
if L <= l and r <= R:
tag[id] += v
tree[id] += v * (r - l + 1)
return
mid = (l + r) // 2
to_down(id, mid - l + 1, r - mid)
update(L, R, v, id * 2, l, mid)
update(L, R, v, id * 2 + 1, mid + 1, r)
tree[id] = tree[id * 2] + tree[id * 2 + 1]
def query(L, R, id, l, r):
if R < l or L > r: return 0
if L <= l and r <= R: return tree[id]
mid = (l + r) // 2
to_down(id, mid - l + 1, r - mid)
return query(L, R, id * 2, l, mid) + query(L, R, id * 2 + 1, mid + 1, r)
n, q = map(int, input().split())
a = [0] + list(map(int, input().split()))
tag = [0] * (n<<2)
tree = [0] * (n << 2)
build(1, 1, n) # 建树
for i in range(q):
op = list(map(int, input().split()))
if op[0] == 1:
x, y, z = op[1:]
update(x, y, z, 1, 1, n)
else:
x, y = op[1:]
print(query(x, y, 1, 1, n))
区间最大值
def bulid(id, l, r):
if l == r:
tree[id] = a[l]
return
mid = (l + r) // 2
bulid(id * 2, l, mid)
bulid(id * 2 + 1, mid + 1, r)
tree[id] = max(tree[id * 2], tree[id * 2 + 1])
def query(L, R, id, l, r):
if R < l or L > r: return -0x3f3f3f3f
if L <= l and r <= R: return tree[id]
mid = (l + r) // 2
return max(query(L, R, id * 2, l, mid), query(L, R, id * 2 + 1, mid + 1, r))
n, q = map(int, input().split())
a = [0] + list(map(int, input().split()))
tree = [0] * (n<<2)
bulid(1, 1, n)
for _ in range(q):
x, y = map(int, input().split())
print(query(x, y, 1, 1, n))
LCA最近公共祖先
import sys
sys.setrecursionlimit(10000)
input = sys.stdin.readline
def dfs(u, fa):
# 预处理
deep[u] = deep[fa] + 1
p[u][0] = fa
for i in range(1, 21):
p[u][i] = p[p[u][i - 1]][i - 1]
for v in G[u]:
if v == fa: continue
dfs(v, u)
def lca(x, y):
if deep[x] < deep[y]:
x, y = y, x
for i in range(20, -1, -1):
if deep[p[x][i]] >= deep[y]:
x = p[x][i]
if x == y:
return x
for i in range(20, -1, -1):
if p[x][i] != p[y][i]:
x, y = p[x][i], p[y][i]
return p[x][0]
n = int(input())
G = [[] for i in range(n + 1)]
deep = [0] * (n + 1) # deep[u]表示节点u的深度
p = [[0] * 21 for i in range(n + 1)] # p[u][i]表示节点u往上走2^i到达的节点
for _ in range(n - 1):
u, v = map(int, input().split())
G[u].append(v)
G[v].append(u)
dfs(1, 0)
Q = int(input())
for _ in range(Q):
x, y = map(int, input().split())
print(lca(x, y))