洛谷 模板汇总 算法基础 python解析

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用欧拉筛过不了会爆内存,只讲究理解算法过程。
2

代码

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

灵神模板—>见代码

解析

题目推荐:2 出现的次数
神奇数 【算法赛】

代码

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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值