蓝桥杯python组备赛(记录个人模板)
文章目录
- 栈
- 队列
- 堆
- 递归装饰器
- 并查集
- 树状数组
- 线段树
- 最近公共祖先LCA
- ST表
- 字典树
- KMP
- manacher
- 跳表(代替C++ STL的set)
- dijkstra
- 总结
栈
用list
代替
队列
用deque
双端队列替代
堆
用heapq
递归装饰器
众所周知,python的递归深度只有1000,根本满足不了大部分1e5以上的数据,当然你可以使用sys.setrecursionlimit(1000000)
扩到1e6,但是这会增加空间消耗,在一些竞赛OJ如codeforces,甚至你还没开始写就爆空间了,甚至会出现莫名其妙的段错误,因此尽可能少用。取而代之的是手写栈,但是手写栈有个问题就是像在写汇编代码,使用装饰器模拟递归栈,就可以正常写递归不怕爆栈了
def bootstrap(func, stk=[]):
def wrapper(*args, **kwargs):
if stk: # 递归一层返回
return func(*args, **kwargs)
else:
# 遍历生成器函数
gen = func(*args, **kwargs)
while True:
if type(gen) is GeneratorType: # 新的生成器函数
stk.append(gen) # 入栈
gen = next(gen) # 递归
else: # 没有新的生成器函数
stk.pop() # 弹出递归入口
if not stk: # 全部遍历完了
break
gen = stk[-1].send(gen)
return gen # 返回答案
return wrapper
不带返回值的调用,函数return改成yield,调用dfs需要加上yield
def solve():
@bootstrap
def dfs(n):
print(n)
if n==0:
yield
yield dfs(n-1)
yield
带返回值的调用,经过实践可以修改装饰器代码使其带返回值,但是很容易出错,因此如果需要返回值,建议直接丢一个list到参数里面返回,因为list传的是引用
def solve():
@bootstrap
def dfs(n,rval):
if n==0:
rval.append(0)
yield
ans=[]
yield dfs(n-1,ans)
rval.append(ans[0]+1)
yield
ans=[]
dfs(5,ans)
print(ans[0]) # 5
并查集
class DSU:
def __init__(self,n):
self.par=[i for i in range(n+1)]
self.siz=[1]*(n+1)
def find(self,x):
if self.par[x]!=x:
self.par[x]=self.find(self.par[x])
return self.par[x]
def merge(self,x,y):
x=self.find(x)
y=self.find(y)
if x==y:
return False
self.par[y]=x
self.siz[x]+=self.siz[y]
return True
def same(self,x,y):
return self.find(x)==self.find(y)
树状数组
class Fenwick:
def __init__(self,n):
self.n=n
self.t=[0]*(n+1)
def add(self,x,y):
while x<=self.n:
self.t[x]+=y
x+=x&-x
def query(self,x):
ans=0
while x:
ans+=self.t[x]
x-=x&-x
return ans
线段树
class SegmentTree:
class Node:
def __init__(self,val=0):
self.sum=val
self.tag=0
def __add__(self,t):
return SegmentTree.Node(self.sum+t.sum)
def __init__(self, n, default=Node()):
self.n=n
self.t=[default]*(n<<2)
def pushup(self,p):
self.t[p]=self.t[p<<1]+self.t[p<<1|1]
def pushtag(self,p,tag,l,r):
self.t[p].tag+=tag
self.t[p].sum+=(r-l+1)*tag
def pushdown(self,p,l,mid,r):
if self.t[p].tag:
self.pushtag(p<<1,self.t[p].tag,l,mid)
self.pushtag(p<<1|1,self.t[p].tag,mid+1,r)
self.t[p].tag=0
def build(self,p,l,r):
if l==r:
self.t[p]=self.Node()
return
mid=l+r>>1
self.build(p<<1,l,mid)
self.build(p<<1|1,mid+1,r)
self.pushup(p)
def modify(self,p,l,r,ql,qr,x):
if(ql<=l and r<=qr):
self.pushtag(p,x,l,r)
return
mid=l+r>>1
self.pushdown(p,l,mid,r)
if ql<=mid:
self.modify(p<<1,l,mid,ql,qr,x)
if qr>mid:
self.modify(p<<1|1,mid+1,r,ql,qr,x)
self.pushup(p)
def modify(self,p,l,r,x,y):
if l==r:
self.t[p].sum+=y
return
mid=l+r>>1
self.pushdown(p,l,mid,r)
if x<=mid:
self.modify(p<<1,l,mid,x,y)
else:
self.modify(p<<1|1,mid+1,r,x,y)
self.pushup(p)
def query(self,p,l,r,ql,qr):
if ql<=l and r<=qr:
return self.t[p]
mid=l+r>>1
self.pushdown(p,l,mid,r)
ans=self.Node()
if ql<=mid:
ans+=self.query(p<<1,l,mid,ql,qr)
if qr>mid:
ans+=self.query(p<<1|1,mid+1,r,ql,qr)
return ans
最近公共祖先LCA
N=int(1e5+10)
adj=[[] for _ in range(N)]
par=[[0]*30 for _ in range(N)]
dep=[0]*N
def dfs(u,fa):
par[u][0]=fa
dep[u]=dep[fa]+1
for i in range(1,dep[u].bit_length()):
par[u][i]=par[par[u][i-1]][i-1]
for v in adj[u]:
if v==fa: continue
dfs(v,u)
def lca(x,y):
if dep[x]<dep[y]:x,y=y,x
while(dep[x]!=dep[y]):
x=par[x][(dep[x]-dep[y]).bit_length()-1]
if x==y: return x
for i in range(dep[x].bit_length()-1,-1,-1):
if par[x][i]!=par[y][i]:
x=par[x][i]
y=par[y][i]
return par[x][0]
ST表
class SparseTable:
def __init__(self,a,n):
self.n=n
self.m=n.bit_length()
self.st=[[0]*self.m for _ in range(n+1)]
for i in range(1,n+1):
self.st[i][0]=a[i]
for j in range(1,self.m):
for i in range(1,n+1):
if i+(1<<j)-1>n:break
self.st[i][j]=max(self.st[i][j-1],self.st[i+(1<<j-1)][j-1])
def query(self,l,r):
j=(r-l+1).bit_length()-1
return max(self.st[l][j],self.st[r-(1<<j)+1][j])
字典树
class Trie:
def __init__(self,n):
self.tr=[[0]*26 for _ in range(n*26)]
self.idx=0
self.cnt=[0]*(n*26)
def insert(self,s):
p=0
for c in s:
c=ord(c)-ord('a')
if not self.tr[p][c]:
idx+=1
self.tr[p][c]=self.idx
p=self.tr[p][c]
self.cnt[p]+=1
def query(self,s):
p=0
for c in s:
c=ord(c)-ord('a')
if not self.tr[p][c]:
return 0
p=self.tr[p][c]
return self.cnt[p]
KMP
def kmp(s):
n = len(s)
nxt = [0] * n
j = 0
for i in range(1, n):
while j > 0 and s[i] != s[j]:
j = nxt[j - 1]
if s[i] == s[j]:
j += 1
nxt[i] = j
return nxt
manacher
def manacher(s):
s = '#' + '#'.join(s) + '#'
n = len(s)
p = [0] * n
r, c = 0, 0
for i in range(n):
if i < r:
p[i] = min(r - i, p[2 * c - i])
while i - p[i] >= 0 and i + p[i] < n and s[i - p[i]] == s[i + p[i]]:
p[i] += 1
if i + p[i] > r:
r = i + p[i]
c = i
return p
跳表(代替C++ STL的set)
感觉是目前最好的平替了,其他平衡树要么太难写没必要,像无旋平衡树这种常数又太大,跳表应该是最好的选择了
MAX_LEVEL = 32
P_FACTOR = 0.25
def random_level() -> int:
lv = 1
while lv < MAX_LEVEL and random.random() < P_FACTOR:
lv += 1
return lv
class SkiplistNode:
__slots__ = 'val', 'forward'
def __init__(self, val: int, max_level=MAX_LEVEL):
self.val = val
self.forward = [None] * max_level
class Skiplist:
def __init__(self):
self.head = SkiplistNode(-1)
self.level = 0
def search(self, target: int) -> bool:
curr = self.head
for i in range(self.level - 1, -1, -1):
# 找到第 i 层小于且最接近 target 的元素
while curr.forward[i] and curr.forward[i].val < target:
curr = curr.forward[i]
curr = curr.forward[0]
# 检测当前元素的值是否等于 target
return curr is not None and curr.val == target
def add(self, num: int) -> None:
update = [self.head] * MAX_LEVEL
curr = self.head
for i in range(self.level - 1, -1, -1):
# 找到第 i 层小于且最接近 num 的元素
while curr.forward[i] and curr.forward[i].val < num:
curr = curr.forward[i]
update[i] = curr
lv = random_level()
self.level = max(self.level, lv)
new_node = SkiplistNode(num, lv)
for i in range(lv):
# 对第 i 层的状态进行更新,将当前元素的 forward 指向新的节点
new_node.forward[i] = update[i].forward[i]
update[i].forward[i] = new_node
def erase(self, num: int) -> bool:
update = [None] * MAX_LEVEL
curr = self.head
for i in range(self.level - 1, -1, -1):
# 找到第 i 层小于且最接近 num 的元素
while curr.forward[i] and curr.forward[i].val < num:
curr = curr.forward[i]
update[i] = curr
curr = curr.forward[0]
if curr is None or curr.val != num: # 值不存在
return False
for i in range(self.level):
if update[i].forward[i] != curr:
break
# 对第 i 层的状态进行更新,将 forward 指向被删除节点的下一跳
update[i].forward[i] = curr.forward[i]
# 更新当前的 level
while self.level > 1 and self.head.forward[self.level - 1] is None:
self.level -= 1
return True
dijkstra
adj=[[] for _ in range(N)]
dis=[inf]*N
def dijkstra():
dis[1]=0
heap=[(0,1)]
while heap:
d,x=heappop(heap)
if dis[x]<d:continue
for y,c in adj[x]:
if d+c<dis[y]:
dis[y]=d+c
heappush(heap,(dis[y],y))
总结
省赛能用到的应该就这么多了,剩下的要么是思维,要么是模拟,要么是各种dp,后续进国赛再深度挖掘一下其他的模板