欢迎关注【Python·算法分类题库】,持续更新中……
字符串(AC自动机、拓展KMP、后缀数组、后缀自动机、回文自动机)
图论(网络流、一般图匹配)
数学(生成函数、莫比乌斯反演、快速傅里叶变换)
数据结构(树链剖分、二维/动态开点线段树、平衡树、可持久化数据结构、树套树、动态树)
排序(归并、快速、桶、堆、基数)
搜索(剪枝、双向BFS、记忆化搜索、迭代加深搜索、启发式搜索)
DP(背包、树形、状压、数位、常见优化)
字符串(哈希、KMP、字典树、Manacher)
图论(欧拉回路、最小生成树、单源最短路及差分约束系统、拓扑序列、二分图匹配、图的连通性问题[割点、桥、强连通分量]、DFS序、最近共同祖先)
数学(排列组合、二项式定理、容斥原理、模意义下的逆元、矩阵计算、高斯消元)
数据结构(ST表、堆[优先队列]、树状数组、线段树、Trie树、并查集、平衡树[利用系统自带的标准库实现简单平衡树])
计算几何(基础计算和基本位置关系判定、概率论、博弈论)
枚举,排序(冒泡、选择、插入)
搜索(DFS+BFS)
贪心
模拟
二分
DP(普通一维问题)
高精度
数据结构(栈、队列、链表)
数学(初等数论)
双指针,前缀和
分治与倍增〔倍增Floyd〕,位运算
三分,01分数规划
搜索
蒙特卡洛树搜索
字符串
最小表示法
数据结构
线性表,二叉树,集合,图的基本应用
并查集,二叉堆与树状数组
主席树,RMQ与LCA
DP
线性DP
数位DP,树形DP,计数DP,区间与环形DP,树与图上的DP
状态压缩DP,动态规划的设计与优化(单调队列DP)
数学
辗转相除法,进阶数论,组合数学与计数,概率与统计,基础线性代数,矩阵加速〔矩阵快速幂〕
散列函数,筛质数,gcd/lcm,快速幂,逆元,扩展欧几里得,费马小定理,更相减损术,博弈论,概率与统计,基础线性代数,矩阵加速(矩阵快速幂)
图论
树〔树的直径〕,最小生成树(Kruskal),连通性问题,分层图,类Floyd算法丨二分图,差分约束,连通分量,网络流,欧拉图
计算几何
点积,叉积,凸包
无评定
入门
普及−
普及/提高−
普及+/提高
提高+/省选−
省选/NOI−
NOI/NOI+/CTSC
排序,快速幂
def is_prime(number):
# 1 不是质数
if number == 1:
return False
# 只检查到根号number的整数,减少迭代次数
i = 2
while i * i <= number:
if number % i == 0:
return False
i += 1
return True
# 测试质数检查函数
print(is_prime(1)) # 预期输出: False
print(is_prime(2)) # 预期输出: True
print(is_prime(17)) # 预期输出: True
print(is_prime(18)) # 预期输出: False
print(is_prime(97)) # 预期输出: True
def is_leap_year(year):
# 根据格里高利历法,年份是闰年的条件
return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0
# 测试闰年检查函数
print(is_leap_year(2000)) # 预期输出: True,因为2000能被400整除
print(is_leap_year(2004)) # 预期输出: True,因为2004能被4整除且不能被100整除
print(is_leap_year(2100)) # 预期输出: False,因为2100能被100整除但不能被400整除
print(is_leap_year(2017)) # 预期输出: False,因为2017不能被4整除
def gcd(a, b):
# 递归计算两个数的最大公约数
return gcd(b, a % b) if b != 0 else a
def lcm(num1, num2):
# 计算两个数的最小公倍数
return num1 * num2 // gcd(num1, num2)
def gcds(nums):
# 计算数组中所有数的最大公约数
g = nums[0]
for num in nums[1:]:
g = gcd(g, num)
return g
def lcms(nums):
# 计算数组中所有数的最小公倍数
l = nums[0]
for num in nums[1:]:
l = lcm(l, num)
return l
# 示例应用
nums = [20, 40, 60, 80]
print(f"数组 {nums} 的最大公约数是:{gcds(nums)}")
print(f"数组 {nums} 的最小公倍数是:{lcms(nums)}")
import math
def combination(n, m):
"""
计算组合数 C(n, m) = n! / (m! * (n-m)!)
"""
if m > n:
return 0
return math.factorial(n) // (math.factorial(m) * math.factorial(n - m))
def permutation(n, m):
"""
计算全排列数 A(n, m) = n! / (n-m)!
"""
if m > n:
return 0
return math.factorial(n) // math.factorial(n - m)
# 示例
n = 5
m = 3
print("组合数 C({}, {}) = {}".format(n, m, combination(n, m)))
print("全排列数 A({}, {}) = {}".format(n, m, permutation(n, m)))
import decimal
# 设置 decimal 来增加精度
decimal.getcontext().prec = 50 # 设置小数点后的精度为50位
def high_precision_int_operations(a, b):
# 将输入转换为整数
int_a = int(a)
int_b = int(b)
# 执行运算
add_result = int_a + int_b
sub_result = int_a - int_b
mul_result = int_a * int_b
div_result = int_a // int_b if int_b != 0 else '除数不能为零' # 使用整数除法
return add_result, sub_result, mul_result, div_result
def high_precision_decimal_operations(a, b):
# 将输入转换为 Decimal 类型
dec_a = decimal.Decimal(a)
dec_b = decimal.Decimal(b)
# 执行运算
add_result = dec_a + dec_b
sub_result = dec_a - dec_b
mul_result = dec_a * dec_b
div_result = dec_a / dec_b if dec_b != 0 else '除数不能为零'
return add_result, sub_result, mul_result, div_result
# 示例
a = '123456789012345678901234567890'
b = '98765432109876543210987654321'
int_results = high_precision_int_operations(a, b)
print("大整数加法结果:", int_results[0])
print("大整数减法结果:", int_results[1])
print("大整数乘法结果:", int_results[2])
print("大整数除法结果:", int_results[3])
decimal_a = '123456789012345678901234567890.1234567890'
decimal_b = '98765432109876543210987654321.9876543211'
decimal_results = high_precision_decimal_operations(decimal_a, decimal_b)
print("高精度小数加法结果:", decimal_results[0])
print("高精度小数减法结果:", decimal_results[1])
print("高精度小数乘法结果:", decimal_results[2])
print("高精度小数除法结果:", decimal_results[3])
将 N 个数从小到大排序后输出。
基于Python内置方法 .sort() 的排序。这个方法通常使用的是 Timsort 算法,一种结合了归并排序和插入排序的混合排序算法。
import sys
input = sys.stdin.read
data = input().split()
N = int(data[0]) # 读取N的值
a = list(map(int, data[1:N+1])) # 读取N个数并转换为整数列表
a.sort() # 对列表进行排序
print(" ".join(map(str, a))) # 将排序后的列表转换为字符串并输出
给定 N N N 个字符串(第 i i i 个字符串长度为 M i M_i Mi,字符串内包含数字、大小写字母,大小写敏感),请求出 N N N 个字符串中共有多少个不同的字符串。
import sys
input = sys.stdin.read
# 读取输入数据
data = input().split()
N = int(data[0])
strings = data[1:N+1]
# 使用集合来自动去除重复的字符串
unique_strings = set(strings)
# 输出不同字符串的数量
print(len(unique_strings))
def is_leap_year(year):
# 根据格里高利历法,年份是闰年的条件
return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0
def count_days(start_year, start_month, start_day, end_year, end_month, end_day):
"""
计算两个日期之间的天数差。
"""
days_count = 0
# 每个月的天数,注意:索引0的位置留空,以方便使用1-12月的索引。
days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
while True:
if start_year == end_year and start_month == end_month and start_day == end_day:
break
start_day += 1
# 检查是否是闰年2月
if is_leap_year(start_year) and start_month == 2:
if start_day > days_in_month[start_month] + 1:
start_month += 1
start_day = 1
else:
if start_day > days_in_month[start_month]:
start_month += 1
start_day = 1
if start_month > 12:
start_month = 1
start_year += 1
days_count += 1
return days_count
# 应用该函数测试几个日期
print(count_days(2020, 1, 1, 2020, 1, 10)) # 从2020年1月1日到2020年1月10日,9天
print(count_days(2020, 2, 27, 2020, 3, 1)) # 跨越闰年2月,3天
def dfs(node, visited, graph):
"""
执行深度优先搜索
:param node: 当前节点
:param visited: 记录已访问节点的集合
:param graph: 图,表示节点间的关系
"""
if node in visited: # 如果节点已经被访问过,则返回
return
visited.add(node) # 标记当前节点为已访问
print(node) # 处理节点,这里以输出为例
# 对当前节点的所有相邻节点进行递归搜索
for next_node in graph[node]:
if next_node not in visited:
dfs(next_node, visited, graph)
# 示例使用
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
visited = set()
dfs('A', visited, graph) # 从节点 A 开始搜索
from collections import deque
def bfs(start, graph):
"""
执行广度优先搜索
:param start: 起始节点
:param graph: 图,表示节点间的关系
"""
visited = set() # 记录已访问节点的集合
queue = deque([start]) # 初始化队列,起始节点入队
while queue:
node = queue.popleft() # 取出队列中的第一个节点
if node in visited: # 如果节点已经被访问过,则跳过
continue
visited.add(node) # 标记为已访问
print(node) # 处理节点,这里以输出为例
# 将所有未访问的相邻节点加入队列
for next_node in graph[node]:
if next_node not in visited:
queue.append(next_node)
# 示例使用
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
bfs('A', graph) # 从节点 A 开始搜索
计算 :2^10 mod 9=7
如:35,5化为二进制后是101,则35=1 * 320 * 1 * 322=1 * 3^1 * 1 * 3^4=243
def quick_pow_mod(a, b, p):
"""
快速幂算法计算 a^b mod p 的结果。
参数:
a (int): 基数。
b (int): 指数。
p (int): 模数。
返回:
int: 计算结果 a^b mod p。
"""
result = 1
base = a % p # 计算模 p 后的基数,避免在计算中数值过大
while b > 0:
if b % 2 == 1: # 如果 b 是奇数,取一个 base 加到结果中
result = (result * base) % p
base = (base * base) % p # 将 base 平方
b //= 2 # 将指数 b 除以 2
return result
# 读取输入值
a, b, p = map(int, input().split())
# 计算 a^b mod p
result = quick_pow_mod(a, b, p)
# 输出格式化的结果
print(f"{a}^{b} mod {p}={result}")
import sys
input = sys.stdin.read
# 读取所有输入
data = input().split()
index = 0
T = int(data[index])
index += 1
# 进行T次测试
for _ in range(T):
stack = []
n = int(data[index])
index += 1
# 对每个测试用例处理n个操作
for _ in range(n):
command = data[index]
index += 1
if command == "push":
x = int(data[index])
index += 1
stack.append(x)
elif command == "pop":
if not stack:
print("Empty")
else:
stack.pop()
elif command == "query":
if not stack:
print("Anguei!")
else:
print(stack[-1])
else: # size command
print(len(stack))
添加图片注释,不超过 140 字(可选)
from collections import deque
import sys
input = sys.stdin.read
data = input().split()
n = int(data[0]) # 读取操作数
queue = deque() # 创建双端队列
index = 1 # 初始化索引,用于跟踪输入数据
for _ in range(n):
op = int(data[index]) # 读取操作码
if op == 1:
x = int(data[index + 1]) # 如果操作是1,读取该操作的数值
queue.append(x) # 将数值加入队列
index += 2 # 更新索引,跳过已读取的数值
elif op == 2:
if not queue:
print("ERR_CANNOT_POP") # 如果队列为空,打印错误信息
else:
queue.popleft() # 如果不为空,从队列前端弹出元素
index += 1
elif op == 3:
if not queue:
print("ERR_CANNOT_QUERY") # 如果队列为空,打印错误信息
else:
print(queue[0]) # 如果不为空,打印队首元素
index += 1
else:
print(len(queue)) # 打印队列的当前大小
index += 1
01背包每个物品只能选一次,而完全背包问题可以多次选一个物品。
def knapsack(max_weight, num_items, volumes, values):
"""
动态规划解决背包问题
:param max_weight: 背包的最大容量
:param num_items: 物品的数量
:param volumes: 各物品的体积列表
:param values: 各物品的价值列表
:return: 最大价值
"""
# 初始化dp数组,长度为背包容量+1,初始值为0
dp = [0] * (max_weight + 1)
# 遍历每一个物品
for i in range(1, num_items + 1):
# 从大到小遍历所有的容量(为了防止在一个阶段中重复使用物品)
for weight in range(max_weight, volumes[i-1] - 1, -1):
# 更新dp数组的值
dp[weight] = max(dp[weight], dp[weight - volumes[i-1]] + values[i-1])
# 输出背包能达到的最大价值
return dp[max_weight]
# 主函数用来接收输入和调用背包函数
def main():
# 输入背包的最大容量和物品数量
max_weight = int(input("请输入背包的最大容量: "))
num_items = int(input("请输入物品的数量: "))
# 初始化物品的体积和价值列表
volumes = []
values = []
# 输入每个物品的体积和价值
for i in range(num_items):
volume, value = map(int, input(f"请输入第{i+1}个物品的体积和价值: ").split())
volumes.append(volume)
values.append(value)
# 计算最大价值
max_value = knapsack(max_weight, num_items, volumes, values)
print("背包可以装入的最大价值是:", max_value)
# 调用主函数
if __name__ == "__main__":
main()
01背包每个物品只能选一次,而完全背包问题可以多次选一个物品。
def knapsack(max_capacity, num_items, volumes, values):
"""
动态规划解决背包问题(正序遍历容量)
:param max_capacity: 背包的最大容量
:param num_items: 物品的数量
:param volumes: 各物品的体积列表
:param values: 各物品的价值列表
:return: 最大价值
"""
# 初始化dp数组,长度为背包容量+1,初始值为0
dp = [0] * (max_capacity + 1)
# 遍历每一个物品
for i in range(num_items):
# 从小到大遍历所有的容量
for weight in range(volumes[i], max_capacity + 1):
# 更新dp数组的值
dp[weight] = max(dp[weight], dp[weight - volumes[i]] + values[i])
# 返回背包能达到的最大价值
return dp[max_capacity]
# 主函数用来接收输入和调用背包函数
def main():
# 输入背包的最大容量和物品数量
max_capacity = int(input("请输入背包的最大容量: "))
num_items = int(input("请输入物品的数量: "))
# 初始化物品的体积和价值列表
volumes = []
values = []
# 输入每个物品的体积和价值
for i in range(num_items):
volume, value = map(int, input(f"请输入第{i+1}个物品的体积和价值: ").split())
volumes.append(volume)
values.append(value)
# 计算最大价值
max_value = knapsack(max_capacity, num_items, volumes, values)
print("背包可以装入的最大价值是:", max_value)
# 调用主函数
if __name__ == "__main__":
main()
给定一个数列,初始为空,请支持下面三种操作:
import sys
import heapq
input = sys.stdin.read
# 读取输入数据
data = input().split()
n = int(data[0])
commands = data[1:]
# 创建一个小根堆来维护数列
heap = []
index = 0
results = []
# 遍历执行所有操作
for _ in range(n):
op = int(commands[index])
if op == 1:
# op=1,加入元素到堆中
x = int(commands[index + 1])
heapq.heappush(heap, x)
index += 2
elif op == 2:
# op=2,输出最小元素
results.append(str(heap[0]))
index += 1
elif op == 3:
# op=3,弹出最小元素
heapq.heappop(heap)
index += 1
# 打印所有需要输出的结果
print("\n".join(results))
查询第 k k k 小的素数
import sys
import numpy as np
input = sys.stdin.read
def get_primes(n):
# 使用numpy创建位数组,初始化所有值为True
is_prime = np.ones(n + 1, dtype=bool)
is_prime[:2] = False # 0和1不是质数
p = 2
while (p * p <= n):
if is_prime[p]:
# 设置p的倍数为非质数
is_prime[p*p:n+1:p] = False
p += 1
# 使用numpy直接获取所有质数
primes = np.nonzero(is_prime)[0]
return primes
# 读取输入并处理
data = input().split()
n = int(data[0]) # 读取n
q = int(data[1]) # 读取查询次数q
primes = get_primes(n) # 获取所有质数
# 处理查询,映射到Python的0开始索引
query_indices = map(int, data[2:])
results = [primes[k-1] for k in query_indices] # 通过列表解析查询质数列表
# 输出结果
print('\n'.join(map(str, results)))
通过维护一个素数列表,并对每个整数进行筛选,但与埃氏筛不同的是,它确保每个合数只被其最小的素因子筛选一次,这使得筛选的过程是线性的,即时间复杂度为O(n)。
def generate_primes(max_num):
"""
使用线性筛算法生成所有小于等于 max_num 的素数。
参数:
- max_num: int, 素数生成的上限。
返回:
- primes: list, 包含所有小于等于 max_num 的素数。
"""
# 初始化标记数组,初始为 False 表示所有数都未被访问
visited = [False] * (max_num + 1)
primes = []
# 从 2 开始遍历每一个数
for i in range(2, max_num + 1):
# 如果当前数 i 未被标记,说明它是素数
if not visited[i]:
primes.append(i)
# 使用当前找到的素数去标记其倍数
for prime in primes:
# 计算当前素数的倍数
composite = i * prime
# 如果超出范围,停止标记
if composite > max_num:
break
# 标记合数
visited[composite] = True
# 如果 i 能被 prime 整除,停止标记当前 i 的更高倍数
if i % prime == 0:
break
return primes
# 应用函数并打印结果
max_number = 100 # 示例中设置 MAXN 为 100
prime_numbers = generate_primes(max_number)
print("Primes up to", max_number, ":", prime_numbers)
P1638 逛画展
博览馆正在展出由世上最佳的 位画家所画的图画。
游客在购买门票时必须说明两个数字, 和 ,代表他要看展览中的第 幅至第 幅画(包含 ,)之间的所有图画,而门票的价钱就是一张图画一元。
Sept 希望入场后可以看到所有名师的图画。当然,他想最小化购买门票的价格。
请求出他购买门票时应选择的 , 数据保证一定有解。
若存在多组解,输出 最小的那组。
def find_minimum_segment(n, m, painters):
# 初始化计数器
counts = [0] * (m + 1)
unique = 0
min_length = float('inf')
result = (-1, -1)
left = 0
# 使用滑动窗口法
for right in range(n):
# 记录当前画家编号
painter = painters[right]
# 如果这个画家之前计数为0,那么我们有了一个新的唯一画家
if counts[painter] == 0:
unique += 1
counts[painter] += 1
# 当我们拥有全部所需的画家时
while unique == m:
# 检查是否是更小的窗口
current_length = right - left + 1
if current_length < min_length:
min_length = current_length
result = (left + 1, right + 1) # 转为 1-based index
# 尝试移动左指针来缩小窗口
counts[painters[left]] -= 1
if counts[painters[left]] == 0:
unique -= 1
left += 1
return result
# 读入数据
import sys
input = sys.stdin.read
data = input().split()
n, m = int(data[0]), int(data[1])
painters = list(map(int, data[2:]))
# 获取最小区间
a, b = find_minimum_segment(n, m, painters)
print(a, b)
题目描述
现有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N , M N,M N,M ,表示共有 N N N 个元素和 M M M 个操作。
接下来 M M M 行,每行包含三个整数 Z i , X i , Y i Z_i,X_i,Y_i Zi,Xi,Yi 。
当 Z i = 1 Z_i=1 Zi=1 时,将 X i X_i Xi 与 Y i Y_i Yi 所在的集合合并。
当 Z i = 2 Z_i=2 Zi=2 时,输出 X i X_i Xi 与 Y i Y_i Yi 是否在同一集合内,是的输出
Y
;否则输出 N
。
输出格式
对于每一个 Z i = 2 Z_i=2 Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y
或者 N
。
import sys
input = sys.stdin.read
class UnionFind:
def __init__(self, size):
# 初始化:每个节点的父节点指向自己,秩(树的高度)为1
self.parent = [i for i in range(size)]
self.rank = [1] * size
def find(self, p):
# 查找根节点,并进行路径压缩
if self.parent[p] != p:
self.parent[p] = self.find(self.parent[p]) # 路径压缩
return self.parent[p]
def union(self, p, q):
# 将两个节点所在的集合合并,按秩合并
rootP = self.find(p)
rootQ = self.find(q)
if rootP != rootQ:
if self.rank[rootP] > self.rank[rootQ]:
self.parent[rootQ] = rootP # 较小的树合并到较大的树
elif self.rank[rootP] < self.rank[rootQ]:
self.parent[rootP] = rootQ
else:
self.parent[rootQ] = rootP
self.rank[rootP] += 1 # 如果相等,选择一个作为根,并增加秩
# 读取输入数据
data = input().split()
index = 0
N = int(data[index])
index += 1
M = int(data[index])
index += 1
uf = UnionFind(N + 1) # 创建并查集实例,节点编号从1开始到N
output = []
for _ in range(M):
Z = int(data[index])
index += 1
X = int(data[index])
index += 1
Y = int(data[index])
index += 1
if Z == 1:
uf.union(X, Y) # 合并操作
elif Z == 2:
# 查询两个节点是否在同一集合
if uf.find(X) == uf.find(Y):
output.append('Y')
else:
output.append('N')
# 输出查询结果
print('\n'.join(output))
正权图请使用dijkstra算法,负权图请使用SPFA算法。
如果图是稀疏的,使用链式前向星更加高效;如果图是稠密的,使用邻接表更加高效。
给定一个 n n n 个点, m m m 条有向边的带非负权图,请你计算从 s s s 出发,到每个点的距离。(单源最短路径)
数据保证你能从 s s s 出发到任意点。
输入格式
第一行为三个正整数 n , m , s n, m, s n,m,s。
第二行起 m m m 行,每行三个非负整数 u i , v i , w i u_i, v_i, w_i ui,vi,wi,表示从 u i u_i ui 到 v i v_i vi 有一条权值为 w i w_i wi 的有向边。
输出格式
输出一行 n n n 个空格分隔的非负整数,表示 s s s 到每个点的距离。
from sys import stdin
from heapq import heappush, heappop
# 读取节点数、边数和起始节点
n, m, s = map(int, input().split())
# 构建图
graph = {i: {} for i in range(1, n + 1)}
for _ in range(m):
u, v, w = map(int, stdin.readline().split())
# 确保存储的是两节点间的最小权重
if v in graph[u]:
graph[u][v] = min(graph[u][v], w)
else:
graph[u][v] = w
# Dijkstra 算法实现
def dijkstra(graph, start):
visit = [False] * (n + 1) # 标记已经访问过的节点
d = [(1 << 31) - 1] * (n + 1) # 存储起点到每个点的最短路径长度,使用 2^31 - 1 表示无限大
d[start] = 0
heap = [(0, start)] # 优先队列
while heap:
dist, u = heappop(heap)
if visit[u]:
continue
visit[u] = True
for v, weight in graph[u].items():
if d[v] > d[u] + weight:
d[v] = d[u] + weight
heappush(heap, (d[v], v))
return d
# 获取最短路径列表并输出
distances = dijkstra(graph, s)
# 输出从起点到每个点的最短路径,从 1 到 n,不包括起始点的位置
print(*distances[1:])
给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
输入格式
第一行包含两个整数 N , M N,M N,M,表示该图共有 N N N 个结点和 M M M 条无向边。
接下来 M M M 行每行包含三个整数 X i , Y i , Z i X_i,Y_i,Z_i Xi,Yi,Zi,表示有一条长度为 Z i Z_i Zi 的无向边连接结点 X i , Y i X_i,Y_i Xi,Yi。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
import sys
input = sys.stdin.read
from heapq import heappop, heappush
class UnionFind:
def __init__(self, size):
self.parent = list(range(size)) # 初始化每个节点的父节点指向自己
self.rank = [0] * size # 初始化秩,用于优化合并过程
def find(self, p):
if self.parent[p] != p:
self.parent[p] = self.find(self.parent[p]) # 路径压缩优化
return self.parent[p]
def union(self, p, q):
rootP = self.find(p)
rootQ = self.find(q)
if rootP != rootQ:
if self.rank[rootP] > self.rank[rootQ]:
self.parent[rootQ] = rootP # 根据秩进行合并,保持树的平衡
elif self.rank[rootP] < self.rank[rootQ]:
self.parent[rootP] = rootQ
else:
self.parent[rootQ] = rootP
self.rank[rootP] += 1 # 如果秩相同,则合并后秩增加
return True
return False
def kruskal(n, edges):
uf = UnionFind(n)
mst_cost = 0
mst_edges = 0
while edges and mst_edges < n - 1:
cost, u, v = heappop(edges) # 从优先队列中取出最小权重的边
if uf.union(u, v): # 如果边连接的两个节点不在同一连通分量中,则加入MST
mst_cost += cost
mst_edges += 1
if mst_edges == n - 1: # 如果MST的边数等于节点数减一,返回总成本
return mst_cost
else:
return 'orz' # 如果无法形成MST,返回'orz'
def main():
data = input().split()
n = int(data[0]) # 节点数量
m = int(data[1]) # 边的数量
edges = []
index = 2
for _ in range(m):
x = int(data[index]) - 1 # 节点编号从0开始
y = int(data[index+1]) - 1
z = int(data[index+2])
index += 3
if x != y: # 避免加入自环
heappush(edges, (z, x, y)) # 将边加入最小堆
result = kruskal(n, edges) # 调用Kruskal算法函数
print(result)
main()
题目描述
给出两个字符串 s 1 s_1 s1 和 s 2 s_2 s2,若 s 1 s_1 s1 的区间 [ l , r ] [l, r] [l,r] 子串与 s 2 s_2 s2 完全相同,则称 s 2 s_2 s2 在 s 1 s_1 s1 中出现了,其出现位置为 l l l。
现在请你求出 s 2 s_2 s2 在 s 1 s_1 s1 中所有出现的位置。
定义一个字符串 s s s 的 border 为 s s s 的一个非 s s s 本身的子串 t t t,满足 t t t 既是 s s s 的前缀,又是 s s s 的后缀。
对于 s 2 s_2 s2,你还需要求出对于其每个前缀 s ′ s' s′ 的最长 border t ′ t' t′ 的长度。
输入格式
第一行为一个字符串,即为 s 1 s_1 s1。
第二行为一个字符串,即为 s 2 s_2 s2。
输出格式
首先输出若干行,每行一个整数,按从小到大的顺序输出 s 2 s_2 s2 在 s 1 s_1 s1 中出现的位置。
最后一行输出 ∣ s 2 ∣ |s_2| ∣s2∣ 个整数,第 i i i 个整数表示 s 2 s_2 s2 的长度为 i i i 的前缀的最长 border 长度。
def compute_prefix_function(pattern):
"""计算KMP算法中的部分匹配表(前缀函数)"""
m = len(pattern)
pi = [0] * m
k = 0
for q in range(1, m):
while k > 0 and pattern[k] != pattern[q]:
k = pi[k - 1]
if pattern[k] == pattern[q]:
k += 1
pi[q] = k
return pi
def kmp_search(text, pattern):
"""使用KMP算法搜索pattern在text中所有出现的起始位置"""
n = len(text)
m = len(pattern)
pi = compute_prefix_function(pattern)
q = 0 # 匹配长度
positions = [] # 记录匹配的开始位置
for i in range(n):
while q > 0 and pattern[q] != text[i]:
q = pi[q - 1]
if pattern[q] == text[i]:
q += 1
if q == m:
positions.append(i - m + 1)
q = pi[q - 1]
return positions
def compute_border_lengths(pattern):
"""计算每个前缀的最长border长度"""
m = len(pattern)
pi = compute_prefix_function(pattern)
return pi
# 读入数据
import sys
input = sys.stdin.read
data = input().split()
s1 = data[0]
s2 = data[1]
# 计算s2在s1中的所有出现位置
positions = kmp_search(s1, s2)
for pos in positions:
print(pos + 1) # 输出位置,从1开始计数
# 计算s2的每个前缀的最长border长度
border_lengths = compute_border_lengths(s2)
print(' '.join(map(str, border_lengths)))
仅55points,待优化。
题目描述
给定 n × n n\times n n×n 的矩阵 A A A,求 A k A^k Ak。
输入格式
第一行两个整数 n , k n,k n,k。
接下来 n n n 行,每行 n n n 个整数,第 i i i 行的第 j j j 的数表示 A i , j A_{i,j} Ai,j。
输出格式
输出 A k A^k Ak
共 n n n 行,每行 n n n 个数,第 i i i 行第 j j j 个数表示 ( A k ) i , j (A^k)_{i,j} (Ak)i,j,每个元素对 1 0 9 + 7 10^9+7 109+7 取模。
import sys
input = sys.stdin.read
MOD = 1000000007
def matrix_mult(A, B, size):
"""优化的矩阵乘法实现,减少了函数调用,并尝试内联一些操作。"""
result = [[0] * size for _ in range(size)]
for i in range(size):
result_row = result[i]
for j in range(size):
sum = 0
A_row = A[i]
for k in range(size):
sum += A_row[k] * B[k][j]
result_row[j] = sum % MOD
return result
def matrix_power(matrix, size, power):
"""使用快速幂算法优化矩阵幂运算。"""
result = [[1 if i == j else 0 for j in range(size)] for i in range(size)]
base = matrix
while power > 0:
if power % 2 == 1:
result = matrix_mult(result, base, size)
base = matrix_mult(base, base, size)
power >>= 1
return result
# 读取和解析输入数据
data = input().split()
n = int(data[0])
k = int(data[1])
index = 2
A = []
for i in range(n):
row = []
for j in range(n):
row.append(int(data[index]))
index += 1
A.append(row)
# 特殊情况处理:k为0时输出单位矩阵
if k == 0:
for i in range(n):
print(' '.join('1' if i == j else '0' for j in range(n)))
else:
result = matrix_power(A, n, k)
for row in result:
print(' '.join(str(x) for x in row))