文章目录
P1226 【模板】快速幂
题目地址:【模板】快速幂
题目分析
快速幂原理:
以这个为例
a
=
2
,
b
=
(
11
)
10
=
(
1011
)
2
a=2,b=(11)_{10}=(1011)_{2}
a=2,b=(11)10=(1011)2,求
2
11
。
2^{11}。
211。
2
11
=
2
8
×
2
0
×
2
2
×
2
1
2^{11}=2^{8} \times 2^{0} \times 2^{2} \times 2^{1}
211=28×20×22×21,幂的次数正好对应
(
1011
)
2
(1011)_{2}
(1011)2
那么只需循环判断即可。
“”“
ans = 1
base = a = 2^1
第一次循环
b = 12 = '0b1011' # 0b是python里的二进制标志
1 = '0b0001'
b & 1 = '0b0001' = 1
ans = ans * base = 1 * 2 = 2
base = 2^2
b = b >> 1 = '0b101'
第二次循环
b & 1 = '0b001' = 1
ans = 2 * 2^2 = 2^3
base = 2^4
b = b >> 1 = '0b10'
第三次循环
b & 1 = '0b00' = 0
base = 2^8
b = b >> 1 = '0b1'
第四次循环
b & 1 = '0b1' = 1
ans = 2^3 * 2^8 = 2^11
base = 2^16
b = b >> 1 = 0 # b=0 退出循环
最后的ans = 2^11.
计算次数由O(b) => log(b)
”“”
通常对于快速幂一般结合取余(mod)来考。了解原理即可
(
A
+
B
)
m
o
d
P
=
(
A
m
o
d
P
+
B
m
o
d
P
)
m
o
d
P
(A+B) \, mod \, P = (A \, mod \, P + B \, mod \,P)\,mod\,P
(A+B)modP=(AmodP+BmodP)modP
(
A
×
B
)
m
o
d
P
=
(
(
A
m
o
d
P
)
×
(
B
m
o
d
P
)
)
m
o
d
P
(A \times B)\,mod\,P=((A\,mod\,P) \times (B\,mod\,P))\,mod\,P
(A×B)modP=((AmodP)×(BmodP))modP
见代码。
代码
# 快速幂
def quickpower(a: int, b: int):
base = a
ans = 1
while b > 0:
if b & 1:
ans *= base
base *= base
b = b >> 1
return ans
# 快速幂应用--取余
def quickmod(a: int, b: int):
base = a
ans = 1
while b > 0:
if b & 1:
ans *= base
ans %= p
base *= base
base %= p
b = b >> 1
return ans
a, b, p = map(int, input().split())
# 只能过一半百分之五十
# ans = quickpower(a, b) % p
# print(f"{a}^{b} mod {p}={ans}")
# AC
ans = quickmod(a, b)
print(f"{a}^{b} mod {p}={ans}")
P3367 【模板】并查集
题目地址:【模板】并查集
题目分析
这个找并集就是找他们共同的祖先。就以题目例题来看.
"""
n = 4, m = 7
fa = [0, 1, 2, 3, 4]
① 1、2的祖先分别为1、2 不相等,输出N
② 将1、2合并,设2为1的祖先,fa = [0, 2, 2, 3, 4]
③ 1、2的祖先分别为2、2 相等,输出Y
④ 将3、4合并,设4为3的祖先,fa = [0, 2, 2, 4, 4]
⑤ 1、4的祖先分别为2、4 不相等,输出N
⑥ 将2、3合并,设3是2的祖先,fa = [0, 2, 3, 4, 4]
⑦ 1的爸爸是2,2的爸爸是3,3的爸爸是4 => 1的祖先是4
4的祖先是4
相等输出Y
"""
这个找祖先的函数递归一下就可以了。
见代码
代码
# 递归找爹
def find(x):
if x == fa[x]:
return x
fa[x] = find(fa[x])
return fa[x]
n, m = map(int, input().split())
fa = [i for i in range(n+1)] # 初始化自己为爹
for _ in range(m):
z, x, y = map(int, input().split())
a, b = find(x), find(y)
if z == 1:
fa[a] = b
else: # z == 2
if a == b:
print('Y')
else:
print('N')
P3383 【模板】线性筛素数
题目地址:【模板】线性筛素数
题目分析
欧拉筛。
注:python用欧拉筛过不了会爆内存,只讲究理解算法过程。
代码
n, q = map(int, input().split())
def GetPrime(n):
Prime = [0]*(n+1)
isPrime = [1]*(n+1)
isPrime[1] = 0
cnt = 0
for i in range(2, n+1):
if isPrime[i]:
cnt += 1
Prime[cnt] = i
for j in range(1, cnt+1):
if i * Prime[j] > n:
continue
isPrime[i*Prime[j]] = 0
if i % Prime[j] == 0:
break
return Prime
prime = GetPrime(n)
for _ in range(q):
k = int(input())
print(prime[k])
P3366 【模板】最小生成树
题目地址:【模板】最小生成树
题目分析
这边介绍两个最小生成树算法:Prim和Kruskal。
两者区别:Prim在稠密图中比Kruskal优,在稀疏图中比Kruskal劣。Prim是以更新过的节点的连边找最小值,Kruskal是直接将边排序。两者都运用了贪心的策略。白话就是:Prim加点、Kruskal加边。
Prim的算法思想和最短路的Dijkstra很像。而Kruskal算法用了并查集(不会见上面并查集的解析)。
两种难易程度看你们自己熟悉的情况,最好两种都要掌握。
其他见代码。
代码
① Prim
from queue import PriorityQueue
def Prim():
global ans, cnt
pq = PriorityQueue()
pq.put((0, 1))
dis[1] = 0
while not pq.empty():
if cnt >= n:
break
d, u = pq.get()
if vis[u]:
continue
vis[u] = True
cnt += 1
ans += d
for i in h[u]: # h[u]里包含的是下一条以同一个起点为起始的边在 rel 数组中的索引
v, w = rel[i][0], rel[i][1]
if w < dis[v]:
dis[v] = w
pq.put((w, v))
n, m = map(int, input().split())
ans = 0
cnte = 0 # 记录下一条以同一个起点为起始的边在 rel 数组中的索引
cnt = 0 # 点的数量
dis = [float('inf')]*(n+1)
vis = [False]*(n+1)
h = [[] for _ in range(n+1)]
rel = [[0, 0, 0] for _ in range(2*m+1)]
for _ in range(m): # 这里的存的方式挺类似于Dijkstra。
u, v, w = map(int, input().split())
cnte += 1
rel[cnte][0] = v
rel[cnte][1] = w
rel[cnte][2] = h[u]
h[u].append(cnte)
cnte += 1
rel[cnte][0] = u
rel[cnte][1] = w
rel[cnte][2] = h[v]
h[v].append(cnte)
Prim()
if cnt == n:
print(ans)
else:
print('orz')
② Kruskal
# 并查集
def find(x):
if x == fa[x]:
return x
else:
fa[x] = find(fa[x])
return fa[x]
def kruskal():
global ans, cnt
for i in range(m):
u = find(rel[i][0])
v = find(rel[i][1])
if u == v: # 在就连上了哇
continue
ans += rel[i][2]
fa[u] = v # 加入并集
cnt += 1
if cnt == n-1: # 已经形成树了
break
n, m = map(int, input().split())
ans = 0
cnt = 0 # 边数
fa = [0]+[i for i in range(1, n+1)]
rel = [[float('inf'), float('inf'), float('inf')] for _ in range(m+1)]
for i in range(1, m+1):
u, v, w = map(int, input().split())
rel[i][0] = u
rel[i][1] = v
rel[i][2] = w
rel.sort(key=lambda x:x[-1])
kruskal()
if cnt == n-1:
print(ans)
else:
print('orz')
P3390 【模板】矩阵快速幂
题目地址:【模板】矩阵快速幂
题目分析
矩阵乘法+快速幂=矩阵快速幂
本题python 过不了,理解算法过程和原理。
代码
def first_step(): # ans *= base
c = [[0]*n for _ in range(n)] # 过渡,防止ans值在计算过程中被覆盖
for k in range(n):
for i in range(n):
for j in range(n):
c[i][j] = (c[i][j] + ans[i][k]*a[k][j]) % mod
for i in range(n):
for j in range(n):
ans[i][j] = c[i][j]
def second_step():
c = [[0]*n for _ in range(n)] # 过渡,防止ans值在计算过程中被覆盖
for k in range(n):
for i in range(n):
for j in range(n):
c[i][j] = (c[i][j] + a[i][k]*a[k][j]) % mod
for i in range(n):
for j in range(n):
a[i][j] = c[i][j]
n, b = map(int, input().split())
a = []
for _ in range(n):
a.append(list(map(int, input().split())))
mod = 10e9+7
ans = [[0]*n for _ in range(n)]
for i in range(n):
ans[i][i] = 1
while b>0:
if b & 1:
first_step()
second_step()
b = b >> 1
for i in range(n):
for j in range(n):
print(int(ans[i][j]%mod), end=' ')
print('')
【模板】单源最短路径
题目分析
代码
【模板】KMP字符串匹配
采用kmp算法模板。
解析
见上述吧,或者看代码。
题目推荐有:契合匹配【算法赛】
代码
def get_next(s: str): # 前缀函数找前缀组
n = len(s)
nnext = [0]*n
for i in range(1, n):
j = nnext[i-1]
while j > 0 and s[i] != s[j]:
j = nnext[j-1]
if s[i] == s[j]:
j += 1
nnext[i] = j
return nnext
def find_occurance(t, s):
cur = s + '#' + t
sz1, sz2 = len(t), len(s)
nnext = get_next(cur)
for i in range(sz2+1, sz1+sz2):
if nnext[i] == sz2:
ans = i-2*sz2
return 1, ans
return 0, 0
【模板】数位dp
灵神模板—>见代码
解析
代码
from functools import lru_cache
# 数位dp
# s = input()
@lru_cache
def f(i: int, mark: int, is_limit: bool, is_Num: bool) -> int:
if i == len(s): # 这里就是判断位数到头了,开始输出结果了
return int(is_Num) # 1/0
res = 0
if not is_Num: # 可以跳过当前数位
res = f(i+1, mark, False, False)
low = 0 if is_Num else 1 # 如果前面没有填数字,必须从 1 开始(因为不能有前导零)
up = int(s[i]) if is_limit else 9 # 如果前面填的数字都和 n 的一样,那么这一位至多填 s[i](否则就超过 n 啦)
for d in range(low, up+1):
if (mark >> d & 1) == 0:
res += f(i+1, mark | (1 << d), is_limit and d == up, True)
return res
# f.cache_clear()