← Back to Blog

26spring做题记录 - May

2026.5做题记录及5月数算月考

May the force be with you

2026.5.3

估计直径

先采取O(n)方法得到一个直径的2-近似T,然后格点化。作l=ϵT/2l=\epsilon *T / \sqrt{2}的网格,并round到中心,因此每个点移动的距离小于ϵdiam(P)\epsilon * diam(P).对网格代表点暴力求直径即可。

#include<bits/stdc++.h>
using namespace std;
struct Point{
    double x,y;
};
double get_dist(Point a, Point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main(){
    int n;
    scanf("%d",&n);
    if(n==1){
        double x,y;
        scanf("%lf %lf",&x,&y);
        printf("%0.6lf",0.0);
        return 0;
    }
    Point points[n];
    for(int i=0;i<n;i++){
        scanf("%lf %lf",&points[i].x,&points[i].y);
    }
    Point p1=points[0];
    double maxd=-1.0;
    for(int i=0;i<n;i++){
        double dist=get_dist(p1,points[i]);
        if(dist>maxd){
            maxd=dist;
        }
    }
    double T=maxd;
    if(T==0.0){
        printf("%0.6lf",0.0);
        return 0;
    }
    double epsilon=0.1;
    double l=epsilon*T/sqrt(2.0);
    map<pair<long long,long long>,Point> grid;
    for(int i=0;i<n;i++){
        long long gx=floor(points[i].x/l);
        long long gy=floor(points[i].y/l);
        grid[make_pair(gx,gy)]=points[i];
    }
    vector<Point> candidates;
    for(auto &[key,pt]:grid){
        candidates.push_back(pt);
    }
    double ans=T;
    int m=candidates.size();
    for(int i=0;i<m;i++){
        for(int j=i+1;j<m;j++){
            double dist=get_dist(candidates[i],candidates[j]);
            if(dist>ans){
                ans=dist;
            }
        }
    }
    printf("%0.6lf",ans);
    return 0;
}

最近点查询

使用四分树。 1.递归建树:对要插入的每个点,判断在四个位置中的哪个,如果原先节点是叶子结点就将其分裂并插入旧点,然后把新点插入对应位置,用0-3表示,用位运算得出点的位置。 2.查询:如果为叶子结点则直接更新答案。否则计算四个节点到目标点的距离,并从小到大排序。优先搜距离近的点,剪枝:如果子节点的最小距离已经是当前答案的1.15倍,则不用搜索。

#include<bits/stdc++.h>
using namespace std;
struct Point{
    double x,y;
    int id;
};
struct QuadNode{
    double cx,cy,size;
    int pointIdx;
    QuadNode *children[4];
    QuadNode(double _cx,double _cy,double _size):cx(_cx),cy(_cy),size(_size),pointIdx(-1){
        for(int i=0;i<4;i++){
            children[i]=nullptr;
        }
    }
};
Point points[100005];
double bestdist;
int bestid;
void insert(QuadNode *node,int pidx){
    if(node->pointIdx==-1){
        node->pointIdx=pidx;
        return;
    }
    double s2=node->size/2.0;
    double s4=node->size/4.0;
    auto getIdx=[&](int idx){
        int res=0;
        if(points[idx].x>node->cx) res|=1;
        if(points[idx].y>node->cy) res|=2;
        return res;
    };
    int subold=node->pointIdx;
    if(subold>=0){
        int oldIdx=getIdx(subold);
        node->pointIdx=-2;
        if(node->children[oldIdx]==nullptr){
            double ncx=node->cx+(oldIdx&1?s4:-s4);
            double ncy=node->cy+(oldIdx&2?s4:-s4);
            node->children[oldIdx]=new QuadNode(ncx,ncy,s2);
        }
        insert(node->children[oldIdx],subold);
    }

    int newIdx=getIdx(pidx);
    if(node->children[newIdx]==nullptr){
        double ncx=node->cx+(newIdx&1?s4:-s4);
        double ncy=node->cy+(newIdx&2?s4:-s4);
        node->children[newIdx]=new QuadNode(ncx,ncy,s2);
    }
    insert(node->children[newIdx],pidx);
}
double get_dist(QuadNode *node,double x,double y){
    if(node==nullptr) return 1e20;
    double dx=max(0.0,abs(node->cx-x)-node->size/2.0);
    double dy=max(0.0,abs(node->cy-y)-node->size/2.0);
    return sqrt(dx*dx+dy*dy);
}
void query(QuadNode *node,double x,double y){
    if(node==nullptr) return;
    if(node->pointIdx>=0){
        double dist=sqrt((points[node->pointIdx].x-x)*(points[node->pointIdx].x-x)+(points[node->pointIdx].y-y)*(points[node->pointIdx].y-y));
        if(dist<bestdist){
            bestdist=dist;
            bestid=points[node->pointIdx].id;
        }
    }
    vector<pair<double,int>> candidates;
    for(int i=0;i<4;i++){
        if(node->children[i]!=nullptr){
            double dist=get_dist(node->children[i],x,y);
            candidates.push_back({dist,i});
        }
    }
    sort(candidates.begin(),candidates.end());
    for(auto &p:candidates){
        if(p.first*1.15>=bestdist) break;
        query(node->children[p.second],x,y);
    }
}
int main(){
    int n,q;
    scanf("%d %d",&n,&q);
    double minx=1e7,maxx=-1e7,miny=1e7,maxy=-1e7;
    for(int i=0;i<n;i++){
        scanf("%lf %lf",&points[i].x,&points[i].y);
        points[i].id=i+1;
        minx=min(minx,points[i].x);
        maxx=max(maxx,points[i].x);
        miny=min(miny,points[i].y);
        maxy=max(maxy,points[i].y);
    }
    double size=max(maxx-minx,maxy-miny)+1;
    QuadNode *root=new QuadNode((minx+maxx)/2.0,(miny+maxy)/2.0,size);
    for(int i=0;i<n;i++){
        insert(root,i);
    }
    for(int i=0;i<q;i++){
        double x,y;
        scanf("%lf %lf",&x,&y);
        bestdist=1e20;
        bestid=-1;
        query(root,x,y);
        printf("%d\n",bestid);
    }
    return 0;
}

最近点对(26)

使用三维四分树找WSPD. 如果两个方块距离大于最优距离则停止。否则计算最近距离。对内部节点进行拆分,注意拆分尺寸较大的内部节点,并递归求距离。

#include<bits/stdc++.h>
using namespace std;
struct Point{
    double x,y,z;
    int id;
};
struct OctNode{
    double cx,cy,cz,size;
    int idx;
    OctNode *children[8];
    OctNode(double _cx,double _cy,double _cz,double _size):cx(_cx),cy(_cy),cz(_cz),size(_size){
        for(int i=0;i<8;i++){
            children[i]=nullptr;
        }
        idx=-1;
    }
};
Point points[100005];
double bestdist=4e18;
void insert(OctNode *node,int pidx){
    if(node->idx==-1){
        node->idx=pidx;
        return;
    }
    double s2=node->size/2.0;
    double s4=node->size/4.0;
    auto getIdx=[&](int idx){
        int res=0;
        if(points[idx].x>node->cx) res|=1;
        if(points[idx].y>node->cy) res|=2;
        if(points[idx].z>node->cz) res|=4;
        return res;
    };
    if(node->idx>=0){
        int subold=node->idx;
        int oldidx=getIdx(subold);
        if(points[subold].x==points[pidx].x&&points[subold].y==points[pidx].y&&points[subold].z==points[pidx].z){
            bestdist=0.0;
            return;
        }
        node->idx=-2;
        if(node->children[oldidx]==nullptr){
            double ncx=node->cx+(oldidx&1?s4:-s4);
            double ncy=node->cy+(oldidx&2?s4:-s4);
            double ncz=node->cz+(oldidx&4?s4:-s4);
            node->children[oldidx]=new OctNode(ncx,ncy,ncz,s2);
        }
        insert(node->children[oldidx],subold);
    }
    int newidx=getIdx(pidx);
    if(node->children[newidx]==nullptr){
        double ncx=node->cx+(newidx&1?s4:-s4);
        double ncy=node->cy+(newidx&2?s4:-s4);
        double ncz=node->cz+(newidx&4?s4:-s4);
        node->children[newidx]=new OctNode(ncx,ncy,ncz,s2);
    }
    insert(node->children[newidx],pidx);
}
double get_dist(Point a,Point b){
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
}
double box_dist(OctNode *u,OctNode *v){
    double dx=max(0.0,abs(u->cx-v->cx)-u->size/2.0-v->size/2.0);
    double dy=max(0.0,abs(u->cy-v->cy)-u->size/2.0-v->size/2.0);
    double dz=max(0.0,abs(u->cz-v->cz)-u->size/2.0-v->size/2.0);
    return dx*dx+dy*dy+dz*dz;
}

void find_pairs(OctNode *u,OctNode *v){
    if(!u||!v||bestdist==0.0) return;
    if(box_dist(u,v)>=bestdist) return;
    if(u->idx>=0&&v->idx>=0){
        if(u->idx!=v->idx){
            double dist=get_dist(points[u->idx],points[v->idx]);
            if(dist<bestdist){
                bestdist=dist;
            }
            return;
        }
    }
    if(u->idx>=0||(v->idx<0&&v->size>=u->size)){
        swap(u,v);
    }
    for(int i=0;i<8;i++){
        if(u->children[i]) find_pairs(u->children[i],v);
    }
}
void solve_internal(OctNode *u){
    if(!u||u->idx>=0) return;
    for(int i=0;i<8;i++){
        solve_internal(u->children[i]);
    }
    for(int i=0;i<8;i++){
        for(int j=i+1;j<8;j++){
            find_pairs(u->children[i],u->children[j]);
        }
    }
}
int main(){
    int n;
    double minx=2e9,miny=2e9,minz=2e9,maxx=-2e9,maxy=-2e9,maxz=-2e9;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%lf %lf %lf",&points[i].x,&points[i].y,&points[i].z);
        points[i].id=i;
        minx=min(minx,points[i].x);
        maxx=max(maxx,points[i].x);
        miny=min(miny,points[i].y);
        maxy=max(maxy,points[i].y);
        minz=min(minz,points[i].z);
        maxz=max(maxz,points[i].z);
    }
    double size=max({maxx-minx,maxy-miny,maxz-minz})+1.0;
    OctNode *root=new OctNode((minx+maxx)/2.0,(miny+maxy)/2.0,(minz+maxz)/2.0,size);
    for(int i=0;i<n;i++){
        insert(root,i);
    }
    solve_internal(root);
    printf("%0.2lf",sqrt(bestdist));
}

向下调整构建大顶堆

对二叉树的每个非叶节点,使其下沉到正确的位置,并递归调整受影响的子树。

def heapify(heap,n,i):
    largest=i
    l=2*i+1
    r=2*i+2
    if(l<n and heap[l]>heap[largest]):
        largest=l
    if(r<n and heap[r]>heap[largest]):
        largest=r
    if(largest!=i):
        heap[i],heap[largest]=heap[largest],heap[i]
        heapify(heap,n,largest)
n=int(input())
heap=list(map(int,input().split()))
for i in range(n//2-1,-1,-1):
    heapify(heap,n,i)
print(*heap)

执行交换操作后的最小汉明距离

并查集。同一个联通分量里面元素可以互相交换,因此比较每个分量的source和target即可。使用Counter统计个数,并贪心匹配。 Counter:记录数组中每个数出现次数,并用字典存储。

from typing import List
from collections import defaultdict,Counter
class Solution:
    def minimumHammingDistance(self, source: List[int], target: List[int], allowedSwaps: List[List[int]]) -> int:
        n=len(source)
        m=len(allowedSwaps)
        fa=[i for i in range(n)]
        size=[1]*n
        def find(x):
            if(fa[x]!=x):
                fa[x]=find(fa[x])
            return fa[x]
        def merge(x,y):
            fx=find(x)
            fy=find(y)
            if(fx==fy):
                return
            if(size[fx]<size[fy]):
                fx,fy=fy,fx
            fa[fy]=fx
            size[fx]+=size[fy]
        for i in range(m):
            merge(allowedSwaps[i][0],allowedSwaps[i][1])
        d=defaultdict(list)
        for i in range(n):
            d[find(i)].append(i)
        ans=0
        for lst in d.values():
            cnt=Counter(source[i] for i in lst)
            for i in lst:
                if(cnt[target[i]]>0):
                    cnt[target[i]]-=1
                else:
                    ans+=1
        return ans

print(Solution().minimumHammingDistance([1,2,3,4], [2,1,4,5], [[0,1],[2,3]]))

哈夫曼编码树

自定义比较顺序。建树使用小根堆,每次取出最小两个作为左右节点,再把它们的父节点放回去。编码直接dfs即可。解码字符串根据搜索得出的表来,解码01串则直接在树上往下搜,搜到底记录答案并返回根部即可。

import heapq
class Node:
    def __init__(self,weight,chars,left=None,right=None):
        self.weight=weight
        self.chars=sorted(list(chars))
        self.min_char=self.chars[0]
        self.left=left
        self.right=right
    def __lt__(self,other):
        if(self.weight!=other.weight):
            return self.weight<other.weight
        return self.min_char<other.min_char
def build(nodes):
    heapq.heapify(nodes)
    while(len(nodes)>1):
        left=heapq.heappop(nodes)
        right=heapq.heappop(nodes)
        new_weight=left.weight+right.weight
        new_chars=left.chars+right.chars
        parent=Node(new_weight,new_chars,left,right)
        heapq.heappush(nodes,parent)
    return nodes[0]
def decode(node,res,codes):
    if(not node.left and not node.right):
        codes[node.chars[0]]=res
        return
    if(node.left):
        decode(node.left,res+'0',codes)
    if(node.right):
        decode(node.right,res+'1',codes)

n=int(input())
nodes=[]
for i in range(n):
    char,freq=input().split()
    freq=int(freq)
    nodes.append(Node(freq,[char]))
root=build(nodes)
char_to_code={}
decode(root,'',char_to_code)
code_to_char={}
while(True):
    try:
        q=input()
        if(q[0] in "01"):
            res=""
            cur=root
            for c in q:
                if(c=='0'):
                    cur=cur.left
                else:
                    cur=cur.right
                if(not cur.left and not cur.right):
                    res+=cur.chars[0]
                    cur=root
        else:
            res=""
            for c in q:
                res+=char_to_code[c]
        print(res)
    except EOFError:
        break

实现 Trie (前缀树)

26叉树。每个节点指向当前字母对应的子节点。

class TreeNode:
    def __init__(self):
        self.children=[None]*26
        self.isEnd=False
class Trie:
    def __init__(self):
        self.root=TreeNode()
    def searchprefix(self,word):
        node=self.root
        for c in word:
            idx=ord(c)-ord('a')
            if(node.children[idx]==None):
                return None
            node=node.children[idx]
        return node
    
    def insert(self, word: str) -> None:
        node=self.root
        for c in word:
            idx=ord(c)-ord('a')
            if(node.children[idx]==None):
                node.children[idx]=TreeNode()
            node=node.children[idx]
        node.isEnd=True

    def search(self, word: str) -> bool:
        node=self.searchprefix(word)
        return node!=None and node.isEnd

    def startsWith(self, prefix: str) -> bool:
        return self.searchprefix(prefix) != None


# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

区域和检索 - 数组可修改

单点修改+区间查询,使用树状数组/线段树。 树状数组:1-i的前缀和每次按lowbit(i)的长度拆分。 线段树:每个节点不断平分区间和

class NumArray:
    def __init__(self, nums: list[int]):
        self.n=len(nums)
        self.nums=nums
        self.tree=[0]*(self.n*4)
        self.build(1,0,self.n-1)
        
    def build(self,node,l,r):
        if(l==r):
            self.tree[node]=self.nums[l]
            return
        mid=(l+r)>>1
        self.build(node*2,l,mid)
        self.build(node*2+1,mid+1,r)
        self.tree[node]=self.tree[node*2]+self.tree[node*2+1]

    def update_tree(self,node,l,r,idx,val):
        if(l==r):
            self.tree[node]=val
            self.nums[idx]=val
            return
        mid=(l+r)>>1
        if(idx<=mid):
            self.update_tree(node*2,l,mid,idx,val)
        else:
            self.update_tree(node*2+1,mid+1,r,idx,val)
        self.tree[node]=self.tree[node*2]+self.tree[node*2+1] 

    def update(self, index: int, val: int) -> None:
        self.update_tree(1,0,self.n-1,index,val)

    def sums(self,node,start,end,l,r):#l-r目标区间,start-end当前区间
        if(l<=start and end<=r):
            return self.tree[node]
        if(end<l or start>r):
            return 0
        mid=(start+end)>>1
        return self.sums(node*2,start,mid,l,r)+self.sums(node*2+1,mid+1,end,l,r)
    
    def sumRange(self, left: int, right: int) -> int:
        return self.sums(1,0,self.n-1,left,right)

附:带lazy tag的线段树实现(区间修改+区间查询) lazy tag即先存下要修改的地方,等访问到了再修改,且能够实现区间完全覆盖时就统一更新。

class NumArray:
    def __init__(self, nums: list[int]):
        self.n=len(nums)
        self.nums=nums
        self.tree=[0]*(self.n*4)
        self.lazy=[0]*(self.n*4)
        self.build(1,0,self.n-1)

    def pushup(self,node):
        self.tree[node]=self.tree[node*2]+self.tree[node*2+1]
    def pushdown(self,node,l,r):
        if(self.lazy[node]!=0):
            mid=(l+r)>>1
            self.tree[node*2]+=(mid-l+1)*self.lazy[node]
            self.tree[node*2+1]+=(r-mid)*self.lazy[node]
            self.lazy[node*2]+=self.lazy[node]
            self.lazy[node*2+1]+=self.lazy[node]
            self.lazy[node]=0    
    def build(self,node,l,r):
        if(l==r):
            self.tree[node]=self.nums[l]
            return
        mid=(l+r)>>1
        self.build(node*2,l,mid)
        self.build(node*2+1,mid+1,r)
        self.pushup(node)

    def update_range(self,node,start,end,l,r,val):
        if(l<=start and end<=r):
            self.tree[node]+=(end-start+1)*val
            self.lazy[node]+=val
            return
        if(end<l or start>r):
            return
        self.pushdown(node,start,end)
        mid=(start+end)>>1
        self.update_range(node*2,start,mid,l,r,val)
        self.update_range(node*2+1,mid+1,end,l,r,val)
        self.pushup(node)

    def sums(self,node,start,end,l,r):#l-r目标区间,start-end当前区间
        if(l<=start and end<=r):
            return self.tree[node]
        if(end<l or start>r):
            return 0
        self.pushdown(node,start,end)
        mid=(start+end)>>1
        return self.sums(node*2,start,mid,l,r)+self.sums(node*2+1,mid+1,end,l,r)
    
    def sumRange(self, left: int, right: int) -> int:
        return self.sums(1,0,self.n-1,left,right)n

平衡二叉树的建立

image image image image image

class AVLNode:
    def __init__(self,val):
        self.val=val
        self.left=None
        self.right=None
        self.height=1
class AVLTree:
    def get_height(self,node):
        if(node==None):
            return 0
        return node.height
    def get_balance(self,node):
        if(node==None):
            return 0
        return self.get_height(node.left)-self.get_height(node.right)
    
    def right_rotate(self,y):
        x=y.left
        T2=x.right
        x.right=y
        y.left=T2
        y.height=max(self.get_height(y.left),self.get_height(y.right))+1
        x.height=max(self.get_height(x.left),self.get_height(x.right))+1
        return x
    def left_rotate(self,x):
        y=x.right
        T2=y.left
        y.left=x
        x.right=T2
        x.height=max(self.get_height(x.left),self.get_height(x.right))+1
        y.height=max(self.get_height(y.left),self.get_height(y.right))+1
        return y
    
    def insert(self,node,val):
        if(node==None):
            return AVLNode(val)
        if(val<node.val):
            node.left=self.insert(node.left,val)
        else:
            node.right=self.insert(node.right,val)
        node.height=1+max(self.get_height(node.left),self.get_height(node.right))
        balance=self.get_balance(node)

        if(balance>1 and val<node.left.val):
            return self.right_rotate(node)
        if(balance<-1 and val>node.right.val):
            return self.left_rotate(node)
        if(balance>1 and val>node.left.val):
            node.left=self.left_rotate(node.left)
            return self.right_rotate(node)
        if(balance<-1 and val<node.right.val):
            node.right=self.right_rotate(node.right)
            return self.left_rotate(node)
        return node
def dfs(node,res):
    if(node==None):
        return
    res.append(node.val)
    dfs(node.left,res)
    dfs(node.right,res)
avl=AVLTree()
root=None
n=int(input())
tree=list(map(int,input().split()))
for val in tree:
    root=avl.insert(root,val)
res=[]
dfs(root,res)
print(' '.join(map(str,res)))

2026.5.5

词梯

使用通配符模式匹配为节点连边。bfs时直接在通配符桶中寻找邻居。剪枝:搜过一个桶之后,可以直接把这个桶删除,因为已经中转过了,不需要再用到。

from collections import deque,defaultdict
n=int(input())
words=[]
for i in range(n):
    words.append(input())
patterns=defaultdict(list)
for i in range(n):
    for j in range(4):
        p=words[i][:j]+"*"+words[i][j+1:]
        patterns[p].append(i)
start,end=input().split()
si=words.index(start)
ei=words.index(end)
q=deque()
q.append(si)
flag=0
pre=[-1]*n
vis=[0]*n
vis[si]=1
while(q):
    node=q.popleft()
    if(node==ei):
        flag=1
        break
    for i in range(4):
        w=words[node][:i]+"*"+words[node][i+1:]
        if(w in patterns):
            for j in patterns[w]:
                if(vis[j]==0):
                    vis[j]=1
                    pre[j]=node
                    q.append(j)
            patterns.pop(w)
if(flag==0):
    print("NO")
else:
    res=[]
    cur=ei
    while(cur!=-1):
        res.append(words[cur])
        cur=pre[cur]
    print(*res[::-1])

有向图判环

有向图判环,需使用三状态标记,判断下一个节点是否在当前路径上。(有可能出现访问了已经访问但不在当前路径上的节点的情况。)(三色dfs) 无向图判环,排除是否为父节点即可。 image Kahn:一个无环的有向图,必然存在至少一个入度为0的节点(没有依赖),拿掉它和它的边之后,剩下的图依然是无环的。(拓扑排序)

n,m=map(int,input().split())
a=[[] for _ in range(n)]
for i in range(m):
    u,v=map(int,input().split())
    a[u].append(v)
vis=[0]*n
flag=0
def dfs(node):
    global flag
    for c in a[node]:
        if(vis[c]==0):
            vis[c]=2
            dfs(c)
        elif(vis[c]==2):
            flag=1
            return
        vis[c]=1
for i in range(n):
    if(vis[i]==0):
        vis[i]=2
        dfs(i)
        vis[i]=1
    if(flag==1):
        break
if(flag==1):
    print("Yes")
else:
    print("No")

骑士周游

采用Warnsdorff’s Rule进行启发式搜索。即在后续格子中选择出路最少、离边界最近的,可以减少后续走了很长的路然后进入死胡同的可能性。

n=int(input())
x0,y0=map(int,input().split())
vis=[[0 for _ in range(n)]for _ in range(n)]
vis[x0][y0]=1
dx=[-2,-1,1,2,2,1,-1,-2]
dy=[-1,-2,-2,-1,1,2,2,1]
flag=0
def get_degree(x,y):
    degree=0
    for i in range(8):
        xx=x+dx[i]
        yy=y+dy[i]
        if(xx>=0 and xx<n and yy>=0 and yy<n and vis[xx][yy]==0):
            degree+=1
    return degree
def get_dist(x,y):
    return (x-n/2.0)**2+(y-n/2.0)**2
def dfs(x,y,step):
    global flag
    if(step==n*n):
        flag=1
        return
    candidates=[]
    for i in range(8):
        xx=x+dx[i]
        yy=y+dy[i]
        if(xx>=0 and xx<n and yy>=0 and yy<n and vis[xx][yy]==0):
            candidates.append((get_degree(xx,yy),-get_dist(xx,yy),xx,yy))
    candidates.sort()
    for _,__,xx,yy in candidates:
        vis[xx][yy]=1
        dfs(xx,yy,step+1)
        if(flag==1):
            return
        vis[xx][yy]=0
dfs(x0,y0,1)
if(flag==1):
    print("success")
else:
    print("fail")

通过质数传送到达终点的最少跳跃次数

bfs. 1.使用类埃氏筛方法得到质数跳转数组。 2.bfs访问过跳转数组之后将其清空 3.bfs不要忘记vis数组

from collections import deque,defaultdict
from typing import List
from math import sqrt
MX=1000001
factors=[[] for _ in range(MX)]
for i in range(2,MX):
    if(not factors[i]):
        for j in range(i,MX,i):
            factors[j].append(i)
class Solution:
    def minJumps(self, nums: List[int]) -> int:
        n=len(nums)
        m=max(nums)
        groups=defaultdict(list)
        for i in range(n):
            for p in factors[nums[i]]:
                groups[p].append(i)
        q=deque()
        q.append((0,0))
        vis=[0]*n
        vis[0]=1
        while(q):
            idx,steps=q.popleft()
            if(idx==n-1):
                return steps
            cur=groups[nums[idx]]
            cur.append(idx+1)
            if(idx-1>=0):
                cur.append(idx-1)
            for i in cur:
                if(i!=idx and not vis[i]):
                    vis[i]=1
                    q.append((i,steps+1))
            cur.clear()
print(Solution().minJumps([1,2,4,6]))

[USACO3.1] 最短网络 Agri-Net

Kruskal:将所有边从小到大排序,依次选择,如果边的两端已经在树上(使用并查集,联通),则跳过。 Prim:选一个点,从连出去的边中选择最短的,把新点加入树中。之后每次选择树上的点和不在树上的点之间的最短边。

import sys
n=int(input())
lst=sys.stdin.read().strip().split()
edges=[]
for i in range(n):
    for j in range(i+1,n):
        idx=i*n+j
        edges.append((int(lst[idx]),i,j))
edges.sort()
m=len(edges)
fa=[i for i in range(n)]
size=[1]*n
def find(x):
    if(fa[x]!=x):
        fa[x]=find(fa[x])
    return fa[x]
def merge(x,y):
    fx=find(x)
    fy=find(y)
    if(fx!=fy):
        if(size[fx]>size[fy]):
            fx,fy=fy,fx
        fa[fx]=fy
        size[fy]+=size[fx]
ans=0
i=0
cnt=0
for i in range(m):
    w,x,y=edges[i]
    if(find(x)!=find(y)):
        merge(x,y)
        ans+=w
        cnt+=1
        if(cnt==n-1):
            break
print(ans)

3维欧氏空间最小生成树(26)

WSPD找所有良好分离的点对,选取代表值连边,对所有边跑一遍Kruskal。

#include<bits/stdc++.h>
using namespace std;
struct Point{
    double x,y,z;
    int id;
};
struct Edge{
    int u,v;
    double w;
    bool operator<(const Edge &e) const {
        return w < e.w;
    }
};
struct OctNode{
    double cx,cy,cz,size;
    int idx;
    int rep_id;
    OctNode *children[8];
    OctNode(double _cx,double _cy,double _cz,double _size):cx(_cx),cy(_cy),cz(_cz),size(_size),rep_id(-1){
        for(int i=0;i<8;i++){
            children[i]=nullptr;
        }
        idx=-1;
    }
};
Point points[100005];
void insert(OctNode *node,int pidx){
    if(node->rep_id==-1) node->rep_id=pidx;
    if(node->idx==-1){
        node->idx=pidx;
        return;
    }
    double s2=node->size/2.0;
    double s4=node->size/4.0;
    auto getIdx=[&](int idx){
        int res=0;
        if(points[idx].x>node->cx) res|=1;
        if(points[idx].y>node->cy) res|=2;
        if(points[idx].z>node->cz) res|=4;
        return res;
    };
    if(node->idx>=0){
        int subold=node->idx;
        int oldidx=getIdx(subold);
        node->idx=-2;
        if(node->children[oldidx]==nullptr){
            double ncx=node->cx+(oldidx&1?s4:-s4);
            double ncy=node->cy+(oldidx&2?s4:-s4);
            double ncz=node->cz+(oldidx&4?s4:-s4);
            node->children[oldidx]=new OctNode(ncx,ncy,ncz,s2);
        }
        insert(node->children[oldidx],subold);
    }
    int newidx=getIdx(pidx);
    if(node->children[newidx]==nullptr){
        double ncx=node->cx+(newidx&1?s4:-s4);
        double ncy=node->cy+(newidx&2?s4:-s4);
        double ncz=node->cz+(newidx&4?s4:-s4);
        node->children[newidx]=new OctNode(ncx,ncy,ncz,s2);
    }
    insert(node->children[newidx],pidx);
}
double get_dist(Point a,Point b){
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
}
double box_dist(OctNode *u,OctNode *v){
    double dx=max(0.0,abs(u->cx-v->cx)-u->size/2.0-v->size/2.0);
    double dy=max(0.0,abs(u->cy-v->cy)-u->size/2.0-v->size/2.0);
    double dz=max(0.0,abs(u->cz-v->cz)-u->size/2.0-v->size/2.0);
    return dx*dx+dy*dy+dz*dz;
}
vector<Edge> edges;
const double s=2.0;
void find_pairs(OctNode *u,OctNode *v){
    if(!u||!v) return;
    if(box_dist(u,v)>=(s*max(u->size,v->size))*(s*max(u->size,v->size))){
        edges.push_back({u->rep_id,v->rep_id,get_dist(points[u->rep_id],points[v->rep_id])});
        return;
    }
    if(u->idx>=0&&v->idx>=0){
        if(u->idx!=v->idx){
            double dist=get_dist(points[u->idx],points[v->idx]);
            edges.push_back({u->idx, v->idx, dist});
        }
        return;
    }
    if(u->idx>=0||(v->idx<0&&v->size>=u->size)){
        swap(u,v);
    }
    for(int i=0;i<8;i++){
        if(u->children[i]) find_pairs(u->children[i],v);
    }
}
void solve_internal(OctNode *u){
    if(!u||u->idx>=0) return;
    for(int i=0;i<8;i++){
        solve_internal(u->children[i]);
    }
    for(int i=0;i<8;i++){
        for(int j=i+1;j<8;j++){
            find_pairs(u->children[i],u->children[j]);
        }
    }
}
int fa[50005],siz[50005];
int find(int x){
    if(fa[x]==x) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
void merge(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx!=fy){
        if(siz[fx]<siz[fy]) swap(fx,fy);
        fa[fy]=fx;
        siz[fx]+=siz[fy];
    }
}
int main(){
    int n;
    double minx=2e9,miny=2e9,minz=2e9,maxx=-2e9,maxy=-2e9,maxz=-2e9;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%lf %lf %lf",&points[i].x,&points[i].y,&points[i].z);
        points[i].id=i;
        minx=min(minx,points[i].x);
        maxx=max(maxx,points[i].x);
        miny=min(miny,points[i].y);
        maxy=max(maxy,points[i].y);
        minz=min(minz,points[i].z);
        maxz=max(maxz,points[i].z);
    }
    double size=max({maxx-minx,maxy-miny,maxz-minz})+1.0;
    OctNode *root=new OctNode((minx+maxx)/2.0,(miny+maxy)/2.0,(minz+maxz)/2.0,size);
    for(int i=0;i<n;i++){
        insert(root,i);
    }
    solve_internal(root);
    int m=edges.size();
    for(int i=0;i<n;i++){
        fa[i]=i;
        siz[i]=1;
    }
    sort(edges.begin(),edges.end());
    int res[n-1][2];
    int cnt=0;
    for(int i=0;i<m;i++){
        if(find(edges[i].u)!=find(edges[i].v)){
            merge(edges[i].u,edges[i].v);
            res[cnt][0]=edges[i].u;
            res[cnt][1]=edges[i].v;
            cnt++;
            if(cnt==n-1) break;
        }
    }
    for(int i=0;i<n-1;i++){
        printf("%d %d\n",res[i][0]+1,res[i][1]+1);
    }
}

2026.5.6 月考

最小新整数

单调栈

t=int(input())
for _ in range(t):
    n,k=input().split()
    k=int(k)
    m=len(n)
    s=[]
    cnt=0
    for c in n:
        while(s and c<s[-1] and cnt<k):
            s.pop()
            cnt+=1
        s.append(c)
    while(cnt<k):
        s.pop()
        cnt+=1
    print("".join(s))

和为给定数

使用字典可能会爆内存。考虑排序后双指针。

from collections import Counter
n=int(input())
a=list(map(int,input().split()))
m=int(input())
s=Counter(a)
res=[]
for c in s:
    if((c<m/2 and c!=m-c and s[c]>0 and s[m-c]>0) or (c==m/2 and s[c]>1)):
        res.append([c,m-c])
if(len(res)==0):
    print("No")
else:
    res.sort()
    print(res[0][0],res[0][1])
n=int(input())
a=list(map(int,input().split()))
m=int(input())
a.sort()
i=0
j=n-1
flag=0
while(i<j):
    if(a[i]+a[j]==m):
        flag=1
        print(a[i],a[j])
        break
    elif(a[i]+a[j]>m):
        j-=1
    else:
        i+=1
if(flag==0):
    print("No")

求二叉树的高度和叶子数目

先用节点和叶子节点的差集找根,然后dfs即可。

n=int(input())
tree=[]
child=set()
leaves=0
for i in range(n):
    x,y=map(int,input().split())
    tree.append([x,y])
    child.add(x)
    child.add(y)
    if(x==-1 and y==-1):
        leaves+=1
for i in range(n):
    if(i not in child):
        root=i
        break
dep=0
def dfs(node,parent,cur):
    global dep
    if(tree[node][0]==-1 and tree[node][1]==-1):
        dep=max(dep,cur)
        return
    for i in tree[node]:
        if(i!=parent and i!=-1):
            dfs(i,node,cur+1)
dfs(root,-1,0)
print(dep,leaves)

地铁换乘

倍增lca以得到两节点之间的距离,然后计算出相遇点即可。

from math import log2
n,t=map(int,input().split())
tree=[[] for _ in range(n+1)]
for i in range(n-1):
    x,y=map(int,input().split())
    tree[x].append(y)
    tree[y].append(x)
p,q,v1,v2=map(int,input().split())
max_log=20
par=[0]*(n+1)
dep=[0]*(n+1)
def dfs(node,parent,depth):
    par[node]=parent
    dep[node]=depth
    for i in tree[node]:
        if(i!=parent):
            dfs(i,node,depth+1)
    return
dfs(t,0,0)
up=[[0 for _ in range(max_log)]for _ in range(n+1)]
for i in range(1,n+1):
    up[i][0]=par[i]
for j in range(1,max_log):
    for i in range(1,n+1):
        up[i][j]=up[up[i][j-1]][j-1]
def lca(x,y):
    if(dep[x]<dep[y]):
        x,y=y,x
    diff=dep[x]-dep[y]
    for i in range(max_log):
        if((diff>>i)&1):
            x=up[x][i]
    if(x==y):
        return x
    for i in range(max_log-1,-1,-1):
        if(up[x][i]!=up[y][i]):
            x=up[x][i]
            y=up[y][i]
    return par[x]
def get(x,k):
    for i in range(max_log):
        if((k>>i)&1):
            x=up[x][i]
    return x
lca_node=lca(p,q)
d1=dep[p]-dep[lca_node]
d2=dep[q]-dep[lca_node]
tot=(d1+d2)//(v1+v2)
s1=tot*v1
ans=0
if(s1<=d1):
    ans=get(p,s1)
else:
    ans=get(q,d1+d2-s1)
print(tot,dep[ans])

排队又来了

[USACO22JAN] Minimizing Haybales P 如果i<j,且hihj|h_i-h_j|>k,则hih_ihjh_j的位置不能互换。因此将hih_i->hjh_j连边,所得即为DAG.根据规则,输出最小拓扑排序即可。 考虑优化复杂度,求每个点入度时,排序并离散化,在从左往右扫描的过程中使用树状数组维护已经扫描过的高度情况。拓扑排序使用线段树优化区间修改入度,即采用线段树记录最小值,每次取根节点并二分查找需要更新入度的区间。

import heapq
from bisect import bisect_right
n,k=map(int,input().split())
a=list(map(int,input().split()))
sorted_a=sorted(a)
idxa=[]
for i in range(n):
    idxa.append((a[i],i))
sorted_idxa=sorted(idxa)
rank_to_h=[0]*(n+1)#rank->高度
pos_to_rank=[0]*(n+1)#原始下标->rank
for i in range(n):
    x,idx=sorted_idxa[i]
    pos_to_rank[idx]=i+1
    rank_to_h[i+1]=x

def lowbit(x):
    return x&(-x)
bit=[0]*(n+1)
def bit_add(idx,v):
    while(idx<=n):
        bit[idx]+=v
        idx+=lowbit(idx)
def bit_query(idx):
    res=0
    while(idx>0):
        res+=bit[idx]
        idx-=lowbit(idx)
    return res
deg=[0]*(n+1)
for i in range(n):
    cur_rank=pos_to_rank[i]
    cur_h=rank_to_h[cur_rank]
    x=bisect_right(sorted_a,cur_h-k-1)
    y=bisect_right(sorted_a,cur_h+k)
    deg[cur_rank]=bit_query(x)+(i-bit_query(y))
    bit_add(cur_rank,1)
tree=[(0,0)]*(4*n)
tag=[0]*(4*n)
def build(node,l,r):
    if(l==r):
        tree[node]=(deg[l],l)
        return
    mid=(l+r)//2
    build(node*2,l,mid)
    build(node*2+1,mid+1,r)
    tree[node]=min(tree[node*2],tree[node*2+1])
def push_up(node,val):
    tag[node]+=val
    tree[node]=(tree[node][0]+val,tree[node][1])
def push_down(node):
    if(tag[node]!=0):
        push_up(node*2,tag[node])
        push_up(node*2+1,tag[node])
        tag[node]=0
def update(node,start,end,l,r,val):
    if(start>r or end<l):
        return
    if(start>=l and end<=r):
        push_up(node,val)
        return
    push_down(node)
    mid=(start+end)//2
    update(node*2,start,mid,l,r,val)
    update(node*2+1,mid+1,end,l,r,val)
    tree[node]=min(tree[node*2],tree[node*2+1])

build(1,1,n)
res=[]
maxm=10**9
for i in range(n):
    min_deg,idx=tree[1]
    res.append(rank_to_h[idx])
    update(1,1,n,idx,idx,maxm)
    h=rank_to_h[idx]
    x=bisect_right(sorted_a,h-k-1)
    y=bisect_right(sorted_a,h+k)
    if(x>=1):
        update(1,1,n,1,x,-1)
    if(y<n):
        update(1,1,n,y+1,n,-1)
print(" ".join(map(str,res)))

2026.5.9

败方树的构建与维护

对每个节点,存储败者与胜者信息,以便继续比较。从叶子往上bfs构建败方树,修改同理。

from collections import deque
n,m=map(int,input().split())
a=list(map(int,input().split()))
class Node:
    def __init__(self,val=0):
        self.val=val
        self.win=val
        self.left=None
        self.right=None
        self.parent=None
leaves=[Node(x) for x in a]
q=deque(leaves)
while(len(q)>1):
    a=q.popleft()
    b=q.popleft()
    cur=Node()
    cur.val=max(a.win,b.win)
    cur.win=min(a.win,b.win)
    cur.left=a
    cur.right=b
    a.parent=b.parent=cur
    q.append(cur)
cur=q.popleft()
root=Node(cur.win)
root.left=cur
cur.parent=root
def bfs():
    res=[]
    qaq=deque()
    qaq.append(root)
    while(qaq and len(res)<n):
        cur=qaq.popleft()
        res.append(cur.val)
        if(cur.left):
            qaq.append(cur.left)
        if(cur.right):
            qaq.append(cur.right)
    return res
res=bfs()
print(*res)
for i in range(m):
    idx,val=map(int,input().split())
    cur=leaves[idx]
    cur.val=cur.win=val
    p=cur.parent
    while(p):
        if(p.right):
            p.val=max(p.left.win,p.right.win)
            p.win=min(p.left.win,p.right.win)
        else:
            p.val=p.win=p.left.win
        p=p.parent
    res=bfs()
    print(*res)

2026.5.8

[CERC 1995] 小木棍

上学期暑假写的,py过不了,用C++重新写了一遍。 剪枝: 1.还剩一根时直接返回 2.前一根不行后一根相同长度的也不行 3.当前长度大于剩余长度直接跳(二分) 4.当前长度刚好填满、刚好等于一根的长度但之前的操作失败了则直接返回失败 5.vis数组回溯改标记,不用每次memset

#include<bits/stdc++.h>
using namespace std;
bool cmp(int a,int b){
    return a>b;
}
int a[70],vis[70],n;
bool dfs(int cnt,int rest,int last,int len){
    if(cnt==1) return true;
    if(rest==0) return dfs(cnt-1,len,0,len);
    int i=last,pre=-1;
    while(i<n){
        if(a[i]>rest){
            i=lower_bound(a,a+n,rest,cmp)-a;
            continue;
        }
        if(vis[i]){
            i++;
            continue;
        }
        if(a[i]==pre){
            i++;
            continue;
        }
        vis[i]=1;
        if(dfs(cnt,rest-a[i],i+1,len)) return true;
        vis[i]=0;
        pre=a[i];
        if(rest==len||rest==a[i]) return false;
        i++;
    }
    return false;
}
int main(){
    int sum=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    sort(a,a+n,cmp);
    memset(vis,0,sizeof(vis));
    for(int i=a[0];i<=sum;i++){
        if(sum%i!=0) continue;
        if(dfs(sum/i,i,0,i)){
            printf("%d\n",i);
            break;
        }
    }
}

2026.5.10

细菌的战争

n=int(input())
for _ in range(n):
    bad,good=map(int,input().split())
    res=0
    while(bad>0):
        res+=1
        bad-=good
        if(bad<=0):
            break
        bad<<=1
        if(bad>1000000):
            bad=1000000
        good=int(1.05*good)
    print(res)

拼点游戏

田忌赛马。cs前后各一个指针,如果遇到相等的情况就从后比较。注意c获得的最大点数不等于s获得的最大点数,因此要跑两遍。

while(True):
    n=int(input())
    if(n==0):
        break
    c=list(map(int,input().split()))
    s=list(map(int,input().split()))
    c.sort(reverse=True)
    s.sort(reverse=True)
    i0=0
    j0=0
    i1=n-1
    j1=n-1
    maxs=0
    maxc=0
    while(i0<=i1 and j0<=j1):
        if(s[i0]>c[j0]):
            maxs+=3
            i0+=1
            j0+=1
        elif(s[i0]<c[j0]):
            maxs+=1
            i1-=1
            j0+=1
        else:
            if(s[i1]>c[j1]):
                maxs+=3
                i1-=1
                j1-=1
            else:
                if(s[i1]<c[j0]):
                    maxs+=1
                    i1-=1
                    j0+=1
                else:
                    maxs+=2
                    i0+=1
                    j0+=1
    i0=0
    j0=0
    i1=n-1
    j1=n-1
    while(i0<=i1 and j0<=j1):
        if(c[i0]>s[j0]):
            maxc+=3
            i0+=1
            j0+=1
        elif(c[i0]<s[j0]):
            maxc+=1
            i1-=1
            j0+=1
        else:
            if(c[i1]>s[j1]):
                maxc+=3
                i1-=1
                j1-=1
            else:
                if(c[i1]<s[j0]):
                    maxc+=1
                    i1-=1
                    j0+=1
                else:
                    maxc+=2
                    i0+=1
                    j0+=1
    print(maxs,4*n-maxc)

from collections import Counter
def check(x,y,z):
    if(x+1==y and y+1==z):
        return True
    if(x==y and y==z):
        return True
def HU(s):
    n=len(s)
    i=0
    while(i<n-2):
        if(not check(s[i],s[i+1],s[i+2])):
            return False
        i+=3
    return True
while(True):
    s=input()
    if(s=='0'):
        break
    s=list(map(int,s.split()))
    n=len(s)
    if(n%3!=2):
        print("XIANGGONG")
        continue
    s.sort()
    a=Counter(s)
    flag=0
    for i in range(1,10):
        if(a[i]>=2):
            t=s.copy()
            t.remove(i)
            t.remove(i)
            if(HU(t)):
                print("HU")
                flag=1
                break
    if(flag==0):
        print("BUHU")

n-gram串频统计

from collections import defaultdict
n=int(input())
s=input()
t=defaultdict(int)
ans=0
for i in range(len(s)-n+1):
    t[s[i:i+n]]+=1
    ans=max(ans,t[s[i:i+n]])
if(ans<=1):
    print("NO")
else:
    res=[]
    for key,val in t.items():
        if(val==ans):
            res.append(key)
    print(ans)
    print("\n".join(res))

TOYS

while(True):
    s=input()
    if(s=='0'):
        break
    n,m,x1,y1,x2,y2=map(int,s.split())
    d={}
    res={}
    # test if y1==y2
    #(0,x1)
    for i in range(0,n):
        u,l=map(int,input().split())
        k=(u-l)/(y1-y2)
        b=(l*y1-u*y2)/(y1-y2)
        d[i]=(u,l,k,b)#x=ky+b
    d[n]=(x2,x2,0,x2)
    for i in range(n+1):
        res[i]=0
    for i in range(m):
        x,y=map(int,input().split())
        for j in range(n+1):
            u,l,k,b=d[j]
            if(x<=max(u,l) and k*y+b>=x):
                res[j]+=1
                break
    for i in range(n+1):
        print(f"{i}: {res[i]}")
    print()

二分查找+叉乘。

while(True):
    s=input()
    if(s=='0'):
        break
    n,m,x1,y1,x2,y2=map(int,s.split())
    d={}
    res={}
    # test if y1==y2
    #(0,x1)
    for i in range(0,n):
        u,l=map(int,input().split())
        d[i]=(u,l,)#x=ky+b
    d[n]=(x2,x2)
    for i in range(n+1):
        res[i]=0
    for i in range(m):
        x,y=map(int,input().split())
        #边界x线段(从上往下),为负则在左侧,为正则在右侧
        left=0
        right=n
        ans=-1
        while(left<=right):
            mid=(left+right)>>1
            u,l=d[mid]#(u-l,y1-y2)x(u-x,y1-y)
            cross=(u-l)*(y1-y)-(y1-y2)*(u-x)
            if(cross<=0):
                ans=mid
                right=mid-1
            else:
                left=mid+1
        res[ans]+=1
    for i in range(n+1):
        print(f"{i}: {res[i]}")
    print()

带通配符的字符串匹配

dp数组记录a的前i位与b的前j位能否匹配。如果a当前位为*,那么从a的前一位匹配到的位置开始到目标串的结束,a的这一位都能匹配上。用tag数组记录a的前一位最早匹配到的位置。

a=" "+input()
b=" "+input()
n=len(a)
m=len(b)
dp=[[0 for _ in range(m)]for _ in range(n)]#a的前i位与b的前j位能否匹配
dp[0][0]=1
tag=[m]*n
for i in range(1,n):
    if(a[i]=="*" and dp[i-1][0]==1):
        dp[i][0]=1
        tag[i]=0
for i in range(1,n):
    for j in range(1,m):
        if(a[i]=="*" and tag[i-1]!=m):
            for k in range(tag[i-1],m):
                dp[i][k]=1
            continue
        elif(dp[i-1][j-1] and (a[i]==b[j] or a[i]=='*' or a[i]=='?')):
            dp[i][j]=1
        if(dp[i][j]):
            tag[i]=min(tag[i],j)
if(dp[n-1][m-1]):
    print("matched")
else:
    print("not matched")

带通配符的字符串匹配 根据dp表的值从后往前回溯。如果遇到则检查前一层的dp并移动指针,否则同时移动两个指针。注意区间长度去掉0,以及和?不能合并到一起。 image

from math import gcd
a=" "+input().strip()
b=" "+input().strip()
n=len(a)
m=len(b)
dp=[[0 for _ in range(m+1)]for _ in range(n+1)]#a的前i位与b的前j位能否匹配
dp[0][0]=1
for i in range(1,n):
    if(a[i]=="*"):
        dp[i][0]=dp[i-1][0]
    else:
        break
for i in range(1,n):
    if(a[i]=="*"):
        first=-1
        if(dp[i][0]):
            first=0
        else:
            for j in range(1,m):
                if(dp[i-1][j]):
                    first=j
                    break
        if(first!=-1):
            for j in range(first,m):
                dp[i][j]=1
    elif(a[i]=='?'):
        for j in range(1,m):
            dp[i][j]=dp[i-1][j-1]
    else:
        for j in range(1,m):
            dp[i][j]=dp[i-1][j-1] and a[i]==b[j]

def get_gcd(arr):
    res=arr[0]
    for i in range(1,len(arr)):
        res=gcd(res,arr[i])
    return res

if(dp[n-1][m-1]):
    print("matched")
    res=[0]*n
    i=n-1
    j=m-1
    while(i>0):
        if(a[i]=="*"):
            if(dp[i-1][j]):
                i-=1
            else:
                res[i]+=1
                j-=1
        else:
            res[i]+=1
            i-=1
            j-=1
    s=[]
    cur=0
    for i in range(1,n):
        if(a[i]=="*" or a[i]=='?'):
            if(i>1 and a[i]!=a[i-1] and cur>0):
                s.append(cur)
                cur=0
            cur+=res[i]
        else:
            if(cur>0):
                s.append(cur)
                cur=0
    if(cur>0):
        s.append(cur)
    if(len(s)==0):
        print(0)
    else:
        print(sum(s)//get_gcd(s))
else:
    print("not matched")

[NOIP 2012 提高组] 同余方程

exgcd:求ax+by=gcd(a,b)的解 ->ax=1(mod b) (ax+by=m有解->gcd(a,b)整除m) https://www.luogu.com.cn/problem/solution/P1082

x=0
y=0
def exgcd(a,b):#ax+by=gcd(a,b)
    global x,y
    if(b==0):
        x=1
        y=0
        return
    exgcd(b,a%b)
    x,y=y,x-(a//b)*y
a,b=map(int,input().split())
exgcd(a,b)
print((x%b+b)%b)

【模板】有理数取余

即求b mod 19260817的乘法逆元。 exgcd求解或者求bmod2b^{mod-2}.(Fermat小定理b(mod1)b^(mod-1)模mod余1) 快读的时候同时取模。

import sys
a=sys.stdin.readline().strip()
b=sys.stdin.readline().strip()
new_a=0
new_b=0
mod=19260817
for i in range(len(a)):
    new_a=(new_a*10+int(a[i]))%mod
for i in range(len(b)):
    new_b=(new_b*10+int(b[i]))%mod
x=0
y=0
if(new_b==0):
    print("Angry!")
    sys.exit(0)
def exgcd(b,m):
    global x,y
    if(m==0):
        x=1
        y=0
        return
    exgcd(m,b%m)
    x,y=y,x-(b//m)*y
exgcd(new_b,mod)
print((new_a*x)%mod)
import sys
a=sys.stdin.readline().strip()
b=sys.stdin.readline().strip()
new_a=0
new_b=0
mod=19260817
for i in range(len(a)):
    new_a=(new_a*10+int(a[i]))%mod
for i in range(len(b)):
    new_b=(new_b*10+int(b[i]))%mod
x=1
if(new_b==0):
    print("Angry!")
    sys.exit(0)
i=mod-2
t=new_b
while(i):
    if(i&1):
        x=x*t%mod
    t=t*t%mod
    i>>=1
print((x*new_a)%mod)

C Looooops

扩展欧几里得Exgcd.

from math import gcd
def exgcd(a,b):
    if(b==0):
        return 1,0
    x,y=exgcd(b,a%b)
    return y,x-(a//b)*y
while(True):
    a,b,c,k=map(int,input().split())
    if(a*a+b*b+c*c+k*k==0):
        break
    '''
    a+cx=b(mod 2^k)
    cx=b-a(mod 2^k)
    cx+(2^k)y=b-a
    t=(b-a)/gcd(c,2^k)
    x=x0t,y=y0t
    cx0+(2^k)y0=gcd(c,2^k)

    (c/gcd)x+(2^k/gcd)y=(b-a)/gcd
    res=x+k*(2^k/gcd)
    '''
    p=1<<k
    g=gcd(c,p)
    diff=(b-a)%p
    if(diff%g!=0):
        print("FOREVER")
        continue
    #b<a:b=b+kp>a,k>(a-b)/p
    x,y=exgcd(c,p)
    p_prime=p//g
    ans=(x*diff//g)%p_prime
    print(ans)

2026.5.12

帮助 Jimmy

dp.注意特判如果下面没有任何平台能够接住时直接落地的情况。

t=int(input())
for _ in range(t):
    n,x0,y0,maxs=map(int,input().split())
    p=[]
    h0=0
    for i in range(n):
        x,y,h=map(int,input().split())
        p.append([h,x,y])
    p.append([y0,x0,x0])
    p.sort(reverse=True)
    MAXM=float('inf')
    dp=[[MAXM for _ in range(2)]for _ in range(n+1)]#dp[i][0]:第i个平台左端点的最短时间,dp[i][1]:右端点
    if(p[-1][0])<=maxs:
        dp[n][0]=dp[n][1]=p[-1][0]
    for i in range(n-1,-1,-1):
        h0=p[i][0]
        curx,cury=p[i][1],p[i][2]
        flag1=0
        flag2=0
        for j in range(i+1,n+1):
            x,y,h=p[j][1],p[j][2],p[j][0]
            if(x<=curx<=y and flag1==0):
                if(h>=h0-maxs):
                    dp[i][0]=min(dp[j][0]+(curx-x),dp[j][1]+(y-curx))+(h0-h)
                flag1=1
            if(x<=cury<=y and flag2==0):
                if(h>=h0-maxs):
                    dp[i][1]=min(dp[j][0]+(cury-x),dp[j][1]+(y-cury))+(h0-h)
                flag2=1
            if(flag1&flag2):
                break
        if(flag1==0):
            if(h0<=maxs):
                dp[i][0]=h0
        if(flag2==0):
            if(h0<=maxs):
                dp[i][1]=h0
    print(min(dp[0][0],dp[0][1]))

积水量

m=int(input())
for _ in range(m):
    n=int(input())
    a=list(map(int,input().split()))
    s=[]
    ans=0
    for i in range(n):
        while(s and a[i]>a[s[-1]]):
            h=s.pop()#水槽底部
            if(s):
                ans+=(min(a[i],a[s[-1]])-a[h])*(i-s[-1]-1)
        s.append(i)
    print(ans)

倒排索引查询

多测清空

n=int(input())
doc=[]
res_glob=set()
for i in range(n):
    a=list(map(int,input().split()))
    s=a[0]
    t=set(a[1:])
    doc.append(t)
    res_glob|=t
m=int(input())
for i in range(m):
    w=list(map(int,input().split()))
    res=res_glob.copy()
    for j in range(n):
        if(w[j]==1):
            res&=doc[j]
    for j in range(n):
        if(w[j]==-1):
            res-=doc[j]
    ans=sorted(list(res))
    if(len(ans)==0):
        print("NOT FOUND")
    else:
        print(*ans)

2026.5.13

Sequence

两两合并n次。两两求前n个的方式是n路归并,即a[0]+b[i],a[1]+b[i],……,a[m-1]+b[i]这m路取最小。m个队头加入优先队列,每次取了哪个就把下一个放入。 注意heapq取出头部用heapq.heappop(),直接用pop是取最后一个。

import heapq
t=int(input())
for _ in range(t):
    m,n=map(int,input().split())
    a=[]
    for i in range(m):
        t=list(map(int,input().split()))
        a.append(sorted(t))
    ans=a[0].copy()
    for k in range(1,m):
        minq=[]
        cur=[]
        for i in range(n):
            heapq.heappush(minq,(ans[i]+a[k][0],i,0))
        for i in range(n):
            sum,idx1,idx2=heapq.heappop(minq)
            cur.append(sum)
            if(idx2+1<n):
                heapq.heappush(minq,(ans[idx1]+a[k][idx2+1],idx1,idx2+1))
        ans=cur.copy()
    print(*ans)
        

最大最小整数

本质冒泡排序。注意cmp_to_key的写法。

from functools import cmp_to_key
def cmp_max(x,y):
    if(x+y>y+x):
        return -1
    elif(x+y==y+x):
        return 0
    else:
        return 1
def cmp_min(x,y):
    if(x+y>y+x):
        return 1
    elif(x+y==y+x):
        return 0
    else:
        return -1
n=int(input())
a=list(input().split())
maxa=sorted(a,key=cmp_to_key(cmp_max))
mina=sorted(a,key=cmp_to_key(cmp_min))
print("".join(maxa),"".join(mina))

变换的迷宫

使用三维vis数组,而不是累加,因为每次抵达这个点的状态不一样。

from collections import deque
dx=[0,1,0,-1]
dy=[1,0,-1,0]
t=int(input())
for _ in range(t):
    r,c,k=map(int,input().split())
    a=[]
    for i in range(r):
        a.append(input())
    x0,y0,x1,y1=0,0,0,0
    for i in range(r):
        for j in range(c):
            if(a[i][j]=='S'):
                x0=i
                y0=j
            if(a[i][j]=='E'):
                x1=i
                y1=j
    q=deque()
    q.append((x0,y0,0))
    flag=0
    vis=[[[0 for _ in range(k)]for _ in range(c)]for _ in range(r)]
    vis[x0][y0][0]=1
    while(q):
        x,y,time=q.popleft()
        if(x==x1 and y==y1):
            print(time)
            flag=1
            break
        for i in range(4):
            xx=x+dx[i]
            yy=y+dy[i]
            if(0<=xx<r and 0<=yy<c):
                if(vis[xx][yy][(time+1)%k]==0):
                    if((time+1)%k==0 or a[xx][yy]!='#'):
                        q.append((xx,yy,time+1))
                        vis[xx][yy][(time+1)%k]=1
    if(flag==0):
        print("Oop!")

最大整数

排序后dp. image

from functools import cmp_to_key
def cmp(x,y):
    if(x+y<y+x):
        return 1
    elif(x+y==y+x):
        return 0
    else:
        return -1
def f(x):
    if(x==""):
        return 0
    else:
        return int(x)
m=int(input())
n=int(input())
a=sorted(list(input().split()),key=cmp_to_key(cmp))
dp=[["" for _ in range(m+1)]for _ in range(n)]#考虑前i个数,组成不超过j位的最大值
for j in range(len(a[0]),m+1):
    dp[0][j]=a[0]
for j in range(1,m+1):
    for i in range(1,n):
        if(len(a[i])>j):
            dp[i][j]=dp[i-1][j]
        else:
            dp[i][j]=str(max(f(dp[i-1][j]),f(dp[i-1][j-len(a[i])]+a[i])))
print(dp[n-1][m])

使数组互补的最少操作次数

差分数组处理整个区间的加减。用数组存储目标和为某个值的情况下需要的总操作数,注意到这只与目标和在哪一段区间有关系,因此可以对目标和数组的一整段进行操作,从而转化为差分做法。

from typing import List
from math import inf
class Solution:
    def minMoves(self, nums: List[int], limit: int) -> int:
        n=len(nums)
        diff=[0]*(limit*2+2)#diff[i]:全部变成i的操作数的差分
        for i in range(n//2):
            x=nums[i]
            y=nums[n-1-i]
            l=min(x,y)+1
            r=max(x,y)+limit
            #[2.l-1]+=2
            diff[2]+=2
            diff[l]-=2
            #[l,r]+=1
            diff[l]+=1
            diff[r+1]-=1
            #去除x+y
            diff[x+y]-=1
            diff[x+y+1]+=1
            #[r+1,limit*2]+2
            diff[r+1]+=2
        ans=inf
        sum=0
        for i in range(2,limit*2+1):
            sum+=diff[i]
            ans=min(ans,sum)
        return ans

2026.5.14

炸鸡排

image

n,k=map(int,input().split())
t=list(map(int,input().split()))
t.sort(reverse=True)
s=sum(t)
idx=0
while(idx<n and t[idx]>s/k):
    s-=t[idx]
    k-=1
    idx+=1
print(f"{s/k:.3f}")

2022决战双十一

n,m=map(int,input().split())
goods=[[-1 for _ in range(m)]for _ in range(n)]
shops=[[]for _ in range(m)]
for i in range(n):
    s=list(input().split())
    for c in s:
        x,y=map(int,c.split(':'))
        goods[i][x-1]=y
for i in range(m):
    s=list(input().split())
    for c in s:
        x,y=map(int,c.split("-"))
        shops[i].append((x,y))
for i in range(m):
    shops[i]=sorted(shops[i],key=lambda x:-x[1])
shop_sum=[0]*m
vis=[[0 for _ in range(m)]for _ in range(n)]
ans=float("inf")
def op(sum):
    for i in range(m):
        for x,y in shops[i]:
            if(shop_sum[i]>=x):
                sum-=y
                break
    return sum
def dfs(sum,cur):
    global ans,shop_sum
    if(cur==n):
        sum-=(sum//300)*50
        ans=min(ans,op(sum))
        return
    for i in range(m):
        if(goods[cur][i]!=-1 and vis[cur][i]==0):
            sum+=goods[cur][i]
            shop_sum[i]+=goods[cur][i]
            vis[cur][i]=1
            dfs(sum,cur+1)
            shop_sum[i]-=goods[cur][i]
            vis[cur][i]=0
            sum-=goods[cur][i]
dfs(0,0)
print(ans)

An Easy Problem

while(True):
    a=int(input())
    if(a==0):
        break
    low=a&(-a)
    new=a+low
    a=a^new
    a=a//low
    a=a//4
    print(new|a)

2026.5.19

生日相同

from collections import defaultdict
n=int(input())
a=defaultdict(list)
for i in range(n):
    idx,m,d=input().split()
    birth=int(m)*100+int(d)
    a[birth].append(idx)
a=dict(list(sorted(a.items(),key=lambda x:x[0])))
for i in a.keys():
    if(len(a[i])>1):
        mon=i//100
        day=i-mon*100
        print(mon,day,*a[i])

满足合法工时的最少人数

from math import ceil
a=list(map(int,input().split(",")))
t=int(input())
l=1
r=max(a)
ans=r
while(l<=r):
    mid=(l+r)>>1
    s=0
    for i in a:
        s+=ceil(i/mid)
    if(s<=t):
        ans=mid
        r=mid-1
    else:
        l=mid+1
print(ans)

虫子的生活

种类并查集/dfs染色

t=int(input())
for qaq in range(1,t+1):
    n,m=map(int,input().split())
    fa=[i for i in range(2*n+2)]
    siz=[1]*(2*n+2)
    def find(x):
        if(fa[x]==x):
            return x
        fa[x]=find(fa[x])
        return fa[x]
    def merge(x,y):
        fx=find(x)
        fy=find(y)
        if(fx==fy):
            return
        if(siz[fx]>siz[fy]):
            fx,fy=fy,fx
        fa[fx]=fy
        siz[fy]+=siz[fx]
    flag=0
    for i in range(m):
        x,y=map(int,input().split())
        if(flag==1):
            continue
        if(find(x)==find(y)):
            flag=1
            continue
        merge(x,y+n)
        merge(x+n,y)
    print(f"Scenario #{qaq}:")
    if(flag==0):
        print("No suspicious bugs found!")
    else:
        print("Suspicious bugs found!")
    print()

[USACO03FALL / HAOI2006] 受欢迎的牛 G

使用Tarjan SCC缩点,一个SCC中的所有奶牛都相互爱慕。然后记录每个SCC的出度,可知如果有一个SCC的出度为0那么其中的所有点为最受欢迎,如果有多个SCC出度为0那么没有最受欢迎的奶牛。

import sys
sys.setrecursionlimit(1000000)
n,m=map(int,input().split())
c=[[]for _ in range(n+1)]
for i in range(m):
    x,y=map(int,input().split())
    c[x].append(y)
low=[0]*(n+1)
dfn=[0]*(n+1)
time=0
s=[]
scc=[]
idx=[0]*(n+1)
def tarjan(k):
    global time
    time+=1
    low[k]=dfn[k]=time
    s.append(k)
    for i in c[k]:
        if(dfn[i]==0):
            tarjan(i)
            low[k]=min(low[k],low[i])
        elif i in s:
            low[k]=min(low[k],dfn[i])
    if(low[k]==dfn[k]):
        tmp=[]
        while(True):
            x=s.pop()
            tmp.append(x)
            idx[x]=len(scc)
            if(x==k):
                break
        scc.append(tmp)
for i in range(1,n+1):
    if(dfn[i]==0):
        tarjan(i)
cnt=len(scc)
out=[0]*cnt
for i in range(1,n+1):
    for j in c[i]:
        if(idx[i]!=idx[j]):
            out[idx[i]]+=1
ans=0
flag=0
for i in range(cnt):
    if(out[i]==0 and ans!=0):
        flag=1
        print(0)
        break
    elif(out[i]==0):
        ans+=len(scc[i])
if(flag==0):
    print(ans)

[IOI 1996 / USACO5.3] 校园网 Network of Schools

使用Tarjan缩点。第一问即求入度为0的点个数,因为这些点没有学校传给它们。第二问特判整个图是否强连通,如果强连通则输出0,否则答案为入度和出度为0的点的个数中的较大值。(第二问要求使整个图强连通最少要增加的边,考虑将入度0和出度0的点两两连接。)

import sys
sys.setrecursionlimit(1000000)
n=int(input())
a=[[]]
for i in range(1,n+1):
    a.append(list(map(int,input().split()))[:-1])
dfn=[0]*(n+1)
low=[0]*(n+1)
time=0
s=[]
scc=[]
idx=[0]*(n+1)
def tarjan(k):
    global time
    time+=1
    low[k]=dfn[k]=time
    s.append(k)
    for i in a[k]:
        if(dfn[i]==0):
            tarjan(i)
            low[k]=min(low[k],low[i])
        elif(i in s):
            low[k]=min(low[k],dfn[i])
    if(low[k]==dfn[k]):
        tmp=[]
        while(True):
            x=s.pop()
            tmp.append(x)
            idx[x]=len(scc)
            if(x==k):
                break
        scc.append(tmp)
for i in range(1,n+1):
    if(dfn[i]==0):
        tarjan(i)
lens=len(scc)
in_deg=[0]*lens
out_deg=[0]*lens
for i in range(1,n+1):
    for j in a[i]:
        if(idx[i]!=idx[j]):
            out_deg[idx[i]]+=1
            in_deg[idx[j]]+=1
ans1=0
ans2=0
for i in range(lens):
    if(in_deg[i]==0):
        ans1+=1
    if(out_deg[i]==0):
        ans2+=1
print(ans1)
if(lens==1):
    print(0)
else:
    print(max(ans1,ans2))

【模板】缩点

Tarjan缩点,将图变为DAG.然后进行拓扑排序,dp最大权值和,注意dp需要在拓扑序上进行以保证正确更新。

from collections import deque
import sys
sys.setrecursionlimit(1000000)
n,m=map(int,input().split())
p=[0]+list(map(int,input().split()))
a=[[]for _ in range(n+1)]
for i in range(m):
    x,y=map(int,input().split())
    a[x].append(y)
dfn=[0]*(n+1)
low=[0]*(n+1)
time=0
s=[]
scc=[]
idx=[0]*(n+1)
val=[]
def tarjan(k):
    global time
    time+=1
    low[k]=dfn[k]=time
    s.append(k)
    for i in a[k]:
        if(dfn[i]==0):
            tarjan(i)
            low[k]=min(low[k],low[i])
        elif(i in s):
            low[k]=min(low[k],dfn[i])
    if(low[k]==dfn[k]):
        tmp=[]
        sum=0
        while(True):
            x=s.pop()
            tmp.append(x)
            sum+=p[x]
            idx[x]=len(scc)
            if(x==k):
                break
        scc.append(tmp)
        val.append(sum)
for i in range(1,n+1):
    if(dfn[i]==0):
        tarjan(i)
lens=len(scc)
in_deg=[0]*lens
dag=[[]for _ in range(lens)]
for i in range(1,n+1):
    for j in a[i]:
        if(idx[i]!=idx[j]):
            in_deg[idx[j]]+=1
            dag[idx[i]].append(idx[j])
q=deque()
dp=[0]*lens
for i in range(lens):
    if(in_deg[i]==0):
        q.append(i)
        dp[i]=val[i]
while(q):
    x=q.popleft()
    for i in dag[x]:
        dp[i]=max(dp[i],dp[x]+val[i])
        in_deg[i]-=1
        if(in_deg[i]==0):
            q.append(i)
print(max(dp))

【模板】最小斯坦纳树

先跑一遍Floyd计算任意两点间的最短路,然后状压dp枚举每个状态(s的子集)的子集,最短长度可以由两个子集的dp值更新。每个状态dp完之后使用dijkstra进行松弛操作,以得到对于状态mask的单源最短路。最后答案取以1-n为根的dp值中的最小值。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
int a[101][101];
int dp[1<<10][101];//j为根,将i中的关键点全部联通的最短距离
int n,m,k;
void dijkstra(int x){
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;
    for(int i=1;i<=n;i++){
        if(dp[x][i]<INF){
            q.push({dp[x][i],i});
        }
    }
    while(!q.empty()){
        auto [dist,u]=q.top();
        q.pop();
        if(dist>dp[x][u]){
            continue;
        }
        for(int v=1;v<=n;v++){
            int w=a[u][v];
            if(dp[x][v]>dp[x][u]+w){
                dp[x][v]=dp[x][u]+w;
                q.push({dp[x][v],v});
            }
        }
    }
}
int main(){
    scanf("%d %d %d",&n,&m,&k);
    memset(a,INF,sizeof(a));
    for(int i=0;i<m;i++){
        int u,v,w;
        scanf("%d %d %d",&u,&v,&w);
        if(a[u][v]>w){
            a[u][v]=w;
            a[v][u]=w;
        }
    }
    int s[k];
    for(int i=0;i<k;i++){
        scanf("%d",&s[i]);
    }
    for(int p=1;p<=n;p++){
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                a[i][j]=min(a[i][j],a[i][p]+a[p][j]);
            }
        }
    }
    memset(dp,INF,sizeof(dp));
    for(int i=1;i<(1<<k);i++){
        if(!(i&(i-1))){//i只有1个1
            for(int j=0;j<k;j++){
                if(i&(1<<j)){
                    dp[i][s[j]]=0;
                    break;
                }
            }
        }
        for(int j=i;j>0;j=(j-1)&i){//枚举i的子集
            for(int p=1;p<=n;p++){
                dp[i][p]=min(dp[i][p],dp[j][p]+dp[i^j][p]);
            }
        }
        dijkstra(i);
    }
    int ans=INF;
    for(int i=1;i<=n;i++){
        ans=min(ans,dp[(1<<k)-1][i]);
    }
    printf("%d\n",ans);
}

简单题

image KD Tree:把 K 维空间不断用垂直于坐标轴的超平面进行二分切割,每个节点代表一个切分点。 image

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int MAXN=200005;
const double alpha=0.75;//替罪羊树平衡因子
struct Node{
    int x,y;
    int v,sum;
    int x1,y1,x2,y2;
    int l,r;
    int size;
} t[MAXN];
int root,tot;
int cur_nodes[MAXN],node_cnt;
bool cmpx(int a,int b){
    return t[a].x<t[b].x;
}
bool cmpy(int a,int b){
    return t[a].y<t[b].y;
}
void push_up(int u){
    int l=t[u].l,r=t[u].r;
    t[u].size=t[l].size+t[r].size+1;
    t[u].sum=t[l].sum+t[r].sum+t[u].v;
    t[u].x1=min({t[u].x,t[l].x1,t[r].x1});
    t[u].y1=min({t[u].y,t[l].y1,t[r].y1});
    t[u].x2=max({t[u].x,t[l].x2,t[r].x2});
    t[u].y2=max({t[u].y,t[l].y2,t[r].y2});
}
void flatten(int u){//替罪羊树思想:不平衡时拍平子树重构
    if(!u) return;
    cur_nodes[node_cnt++]=u;
    flatten(t[u].l);
    flatten(t[u].r);
}
int build(int l,int r,int dim){
    if(l>r) return 0;
    int mid=(l+r)>>1;
    if(dim==0){
        nth_element(cur_nodes+l,cur_nodes+mid,cur_nodes+r+1,cmpx);
    }
    else{
        nth_element(cur_nodes+l,cur_nodes+mid,cur_nodes+r+1,cmpy);
    }
    int u=cur_nodes[mid];
    t[u].l=build(l,mid-1,dim^1);
    t[u].r=build(mid+1,r,dim^1);
    push_up(u);
    return u;
}
void insert(int &u,int p,int dim){
    if(!u){
        u=p;
        push_up(u);
        return;
    }
    if(dim==0){
        if(t[p].x<t[u].x){
            insert(t[u].l,p,dim^1);
        }
        else{
            insert(t[u].r,p,dim^1);
        }
    }
    else{
        if(t[p].y<t[u].y){
            insert(t[u].l,p,dim^1);
        }
        else{
            insert(t[u].r,p,dim^1);
        }
    }
    push_up(u);
    if(t[u].size*alpha<max(t[t[u].l].size,t[t[u].r].size)){
        node_cnt=0;
        flatten(u);
        u=build(0,node_cnt-1,dim);
    }
}
int query(int u,int x1,int y1,int x2,int y2){
    if(!u) return 0;
    if(t[u].x1>=x1 && t[u].y1>=y1 && t[u].x2<=x2 && t[u].y2<=y2){
        return t[u].sum;
    }
    if(t[u].x2<x1 || t[u].y2<y1 || t[u].x1>x2 || t[u].y1>y2){
        return 0;
    }
    int res=0;
    if(t[u].x>=x1 && t[u].x<=x2 && t[u].y>=y1 && t[u].y<=y2){
        res+=t[u].v;
    }
    res+=query(t[u].l,x1,y1,x2,y2);
    res+=query(t[u].r,x1,y1,x2,y2);
    return res;
}
int main(){
    t[0].x1=t[0].y1=2e9;
    t[0].x2=t[0].y2=-2e9;
    t[0].sum=t[0].size=0;
    int n;
    scanf("%d",&n);
    int op;
    int last_ans=0;
    while(scanf("%d",&op) && op!=3){
        if(op==1){
            int x,y,A;
            scanf("%d %d %d",&x,&y,&A);
            x^=last_ans;
            y^=last_ans;
            A^=last_ans;
            tot++;
            t[tot].x=x;
            t[tot].y=y;
            t[tot].v=t[tot].sum=A;
            insert(root,tot,0);
        }
        else if(op==2){
            int x1,y1,x2,y2;
            scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
            x1^=last_ans;
            y1^=last_ans;
            x2^=last_ans;
            y2^=last_ans;
            last_ans=query(root,x1,y1,x2,y2);
            printf("%d\n",last_ans);
        }
    }
}

2026.5.20 月考

(from 群友fxc)

Huffman编码树

贪心地可知,Huffman编码的方式可使题目所求式子最小。

import heapq
n=int(input())
a=list(map(int,input().split()))
q=[]
for i in a:
    heapq.heappush(q,i)
ans=0
while(len(q)>1):
    x=heapq.heappop(q)
    y=heapq.heappop(q)
    ans+=x+y
    heapq.heappush(q,x+y)
print(ans)

兔子与樱花

使用Floyd求出全图最短路。记录路径的方法是对每一组起点和终点,记录起点下一步的点,并在松弛操作时更新。

from collections import defaultdict
from math import inf
p=int(input())
d=defaultdict(int)
name=[]
for i in range(p):
    s=input()
    d[s]=i
    name.append(s)
a=[[inf for _ in range(p)]for _ in range(p)]
q=int(input())
next=[[0 for _ in range(p)]for _ in range(p)]
for i in range(q):
    x,y,z=input().split()
    idxx=d[x]
    idxy=d[y]
    a[idxx][idxy]=min(a[idxx][idxy], int(z))
    a[idxy][idxx]=min(a[idxy][idxx], int(z))
for i in range(p):
    for j in range(p):
        if(i!=j and a[i][j]!=inf):
            next[i][j]=j
        else:
            next[i][j]=-1
for k in range(p):
    for i in range(p):
        for j in range(p):
            if(a[i][k]+a[k][j]<a[i][j]):
                a[i][j]=a[i][k]+a[k][j]
                next[i][j]=next[i][k]
r=int(input())
for i in range(r):
    x,y=input().split()
    idxx=d[x]
    idxy=d[y]
    cur=idxx
    nxt=next[idxx][idxy]
    while(cur!=idxy):
        print(f"{name[cur]}->({a[cur][nxt]})->",end="")
        cur=nxt
        nxt=next[cur][idxy]
    print(name[idxy])

两座孤岛最短距离

dfs求连通块找到一个孤岛,并从这个岛上每个点对另一个岛bfs找最短路。

from collections import deque
n=int(input())
a=[]
vis=[[0 for _ in range(n)]for _ in range(n)]
dx=[-1,0,1,0]
dy=[0,1,0,-1]
q=deque()
def dfs(x,y):
    for i in range(4):
        xx=x+dx[i]
        yy=y+dy[i]
        if(xx>=0 and xx<n and yy>=0 and yy<n):
            if(vis[xx][yy]==0 and a[xx][yy]=="1"):
                vis[xx][yy]=1
                q.append((xx,yy,0))
                dfs(xx,yy)

for i in range(n):
    a.append(list(input()))
flag=0
for i in range(n):
    if(flag==1):
        break
    for j in range(n):
        if(a[i][j]=="1"):
            vis[i][j]=1
            q.append((i,j,0))
            dfs(i,j)
            flag=1
            break
for i in range(n):
    for j in range(n):
        if(vis[i][j]==1):
            a[i][j]='0'
while(q):
    x,y,dis=q.popleft()
    if(a[x][y]=="1"):
        print(dis-1)
        break
    for i in range(4):
        xx=x+dx[i]
        yy=y+dy[i]
        if(xx>=0 and xx<n and yy>=0 and yy<n):
            if(vis[xx][yy]==0):
                vis[xx][yy]=1
                q.append((xx,yy,dis+1))

宝藏二叉树

树形dp.两个dp数组分别记录以i为根,选/不选i时整棵子树的最大值。从下往上更新。

n=int(input())
a=[0]+list(map(int,input().split()))
dp1=[0 for _ in range(n+1)]#以i为根,选i
dp2=[0 for _ in range(n+1)]#以i为根,不选i
for i in range(n,0,-1):
    left=i<<1
    right=(i<<1)|1
    dp1[i]=a[i]
    if(left<=n):
        dp1[i]+=dp2[left]
        dp2[i]+=max(dp1[left],dp2[left])
    if(right<=n):
        dp1[i]+=dp2[right]
        dp2[i]+=max(dp1[right],dp2[right])
print(max(dp1[1],dp2[1]))

Catenyms

对每个单词建立从第一个字母指向最后一个字母的有向边,题目转化为求欧拉路径。通过入度与出度判断是否存在合法的路径以及起点,然后使用Hierholzer算法,dfs搜索,每次经过一条边即删除,并在当前点无出边时将该点倒序加入答案路径。

from collections import defaultdict
import sys
sys.setrecursionlimit(10**7)
t=int(input())
for _ in range(t):
    n=int(input())
    words=[]
    for i in range(n):
        tmp=input()
        words.append(tmp)
    words.sort()
    a=defaultdict(list)
    in_deg=defaultdict(int)
    out_deg=defaultdict(int)
    for i in range(n):
        start=words[i][0]
        end=words[i][-1]
        a[start].append((end,words[i],i))
        out_deg[start]+=1
        in_deg[end]+=1
    flag=0
    m=len(in_deg)
    start=""
    end=""
    for i in a.keys():
        if(out_deg[i]-in_deg[i]==1):
            if(start==""):
                start=i
            else:
                flag=1
                break
        elif(in_deg[i]-out_deg[i]==1):
            if(end==""):
                end=i
            else:
                flag=1
                break
        elif(in_deg[i]!=out_deg[i]):
            flag=1
            break
    if(flag==1):
        print("***")
        continue
    if(start==""):
        start=min(a.keys())
    ans=[]
    vis=[0]*n
    def dfs(x):
        for i in range(len(a[x])):
            end,word,idx=a[x][i]
            if(vis[idx]==0):
                vis[idx]=1
                dfs(end)
                ans.append(word)
    dfs(start)
    if(len(ans)!=n):
        print("***")
    else:
        ans.reverse()
        print(".".join(ans))

力场叠加模拟

使用lazy tag的线段树。

class SegmentTree:
    def __init__(self,n,nums):
        self.n=n
        self.nums=nums
        self.tree=[0]*(n*4)
        self.lazy=[0]*(n*4)
        self.build(1,1,n)
    def pushup(self,node):
        self.tree[node]=max(self.tree[node*2],self.tree[node*2+1])
    def pushdown(self,node,l,r):
        if(self.lazy[node]!=0):
            mid=(l+r)>>1
            self.tree[node*2]+=self.lazy[node]
            self.tree[node*2+1]+=self.lazy[node]
            self.lazy[node*2]+=self.lazy[node]
            self.lazy[node*2+1]+=self.lazy[node]
            self.lazy[node]=0
    def build(self,node,l,r):
        if(l==r):
            self.tree[node]=self.nums[l]
            return
        mid=(l+r)>>1
        self.build(node*2,l,mid)
        self.build(node*2+1,mid+1,r)
        self.pushup(node)
    def update_range(self,node,start,end,l,r,val):
        if(l<=start and end<=r):
            self.tree[node]+=val
            self.lazy[node]+=val
            return
        if(end<l or start>r):
            return
        self.pushdown(node,start,end)
        mid=(start+end)>>1
        self.update_range(node*2,start,mid,l,r,val)
        self.update_range(node*2+1,mid+1,end,l,r,val)
        self.pushup(node)
    def query(self,node,start,end,l,r):
        if(l<=start and end<=r):
            return self.tree[node]
        if(end<l or start>r):
            return 0
        self.pushdown(node,start,end)
        mid=(start+end)>>1
        return max(self.query(node*2,start,mid,l,r),self.query(node*2+1,mid+1,end,l,r))
n,q=map(int,input().split())
tree=SegmentTree(n,[0]*(n+1))
for _ in range(q):
    s=input().split()
    op=s[0]
    if(op=='Add'):
        l,r,v=map(int,s[1:])
        tree.update_range(1,1,n,l,r,v)
    elif(op=="Query"):
        l,r=map(int,s[1:])
        print(tree.query(1,1,n,l,r))

2026.5.21

高维欧氏空间最小完美匹配(26)

Tree embedding->把数据放入2-HST中,树上距离近似替代两点距离。 使用随机平移四分树以保证相近的点被分到两个格子的概率很小。子树内的点优先和同一子树匹配。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<cmath>
#include<random>
#include<map>
using namespace std;
int n,d;
double points[3005][105];
double shift[105];
double match[3005];
int solve(const vector<int> &points_idx,const vector<double> &mins,double len){
    if(points_idx.empty()) return -1;
    if(points_idx.size()==1) return points_idx[0];
    if(len<1e-3){
        int tmp=-1;
        for(int i:points_idx){//内部两两匹配
            if(tmp==-1){
                tmp=i;
            }
            else{
                match[tmp]=i;
                match[i]=tmp;
                tmp=-1;
            }
        }
        return tmp;
    }
    double mid=len/2.0;
    map<vector<int>,vector<int>> sub_blocks;//子块
    for(int i:points_idx){
        vector<int> cell(d);
        for(int j=0;j<d;j++){
            if(points[i][j]>=mins[j]+mid){
                cell[j]=1;
            }
            else cell[j]=0;
        }
        sub_blocks[cell].push_back(i);
    }
    vector<int> unmatched;
    for(auto &block:sub_blocks){
        const vector<int>& cell_id=block.first;
        const vector<int>& sub_points=block.second;
        vector<double> sub_mins(d);
        for(int i=0;i<d;i++){
            sub_mins[i]=mins[i]+cell_id[i]*mid;
        }
        int res=solve(sub_points,sub_mins,mid);
        if(res!=-1){
            unmatched.push_back(res);
        }
    }
    int tmp=-1;
    for(int i:unmatched){
        if(tmp==-1){
            tmp=i;
        }
        else{
            match[tmp]=i;
            match[i]=tmp;
            tmp=-1;
        }
    }
    return tmp;
}
int main(){
    scanf("%d %d",&n,&d);
    vector<int> idx(n);
    for(int i=0;i<n;i++){
        idx[i]=i;
        for(int j=0;j<d;j++){
            scanf("%lf",&points[i][j]);
        }
    }
    double minm=-1e8-100.0;
    double maxm=1e8+100.0;
    mt19937_64 rng(250903);
    double delta=1LL<<29;
    uniform_real_distribution<double> dist_shift(0.0,delta*0.1);
    vector<double> base_mins(d);
    for(int i=0;i<d;i++){
        shift[i]=dist_shift(rng);
        base_mins[i]=minm-shift[i];
    }
    solve(idx,base_mins,delta*2.0);
    for(int i=0;i<n;i++){
        printf("%d ",(int)match[i]+1);
    }
}

高维欧氏空间最小生成树(加强)(26)

独立采样m棵随机平移四分树,对每棵树的每个节点,建立星形图并加入候选边集中,最后对边集跑一遍MST.

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<cmath>
#include<random>
#include<map>
using namespace std;
int n,d;
double points[20005][105];
struct Edge{
    int u,v;
    double w;
    bool operator<(const Edge& other) const{
        return w<other.w;
    }
};
vector<Edge> cand_edges;
double get_dist(int i,int j){
    double dist=0;
    for(int k=0;k<d;k++){
        double diff=points[i][k]-points[j][k];
        dist+=diff*diff;
    }
    return sqrt(dist);
}
void build_spanner_edges(const vector<int>& points_idx,const vector<double>& mins,double len){
    if(points_idx.size()<=1) return;
    int center=points_idx[0];
    for(int i=1;i<points_idx.size();i++){
        int to=points_idx[i];
        cand_edges.push_back({center,to,get_dist(center,to)});
    }
    if(len<1.0) return;
    double mid=len/2.0;
    map<vector<int>,vector<int>> sub_blocks;
    for(int i:points_idx){
        vector<int> cell(d);
        for(int j=0;j<d;j++){
            if(points[i][j]>=mins[j]+mid){
                cell[j]=1;
            }
            else cell[j]=0;
        }
        sub_blocks[cell].push_back(i);
    }
    for(auto &block:sub_blocks){
        const vector<int>& cell_id=block.first;
        const vector<int>& sub_points=block.second;
        vector<double> sub_mins(d);
        for(int i=0;i<d;i++){
            sub_mins[i]=mins[i]+cell_id[i]*mid;
        }
        build_spanner_edges(sub_points,sub_mins,mid);
    }
}
int fa[200005];
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
bool merge(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx==fy) return false;
    fa[fx]=fy;
    return true;
}
int main(){
    scanf("%d %d",&n,&d);
    vector<int> idx(n);
    for(int i=0;i<n;i++){
        idx[i]=i;
        for(int j=0;j<d;j++){
            scanf("%lf",&points[i][j]);
        }
    }
    double global_min=-1000.0;
    double delta=1LL<<30;
    mt19937_64 rng(111);
    uniform_real_distribution<double> dist_shift(0.0,delta*0.2);
    int m=15;
    for(int t=0;t<m;t++){
        vector<double> mins(d);
        for(int i=0;i<d;i++){
            double shift_v=dist_shift(rng);
            mins[i]=global_min-shift_v;
        }
        build_spanner_edges(idx,mins,delta);
    }
    sort(cand_edges.begin(),cand_edges.end());
    for(int i=0;i<n;i++) fa[i]=i;
    int cnt=0;
    for(const Edge edge:cand_edges){
        if(merge(edge.u,edge.v)){
            printf("%d %d\n",edge.u+1,edge.v+1);
            cnt++;
            if(cnt==n-1) break;
        }
    }
    return 0;
}

2026.5.25

高维欧氏空间范围点数查询

对网格进行随机平移,dfs搜索合法的网格,将点数相加。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<random>
#include<string>
#include<cmath>
#include<vector>
#include<map>
using namespace std;
int d,n,m,r;
double points[100005][20];
double shift[20];
map<vector<int>,int> grid_counts;
double len_l;
vector<int> cur_grid;
vector<double> q_coords;
int ans;
void dfs(int dim,double cur_dist){
    if(cur_dist>r*r) return;
    if(dim==d){
        auto it=grid_counts.find(cur_grid);
        if(it!=grid_counts.end()){
            ans+=it->second;
        }
        return;
    }
    int center_id=(int)floor((q_coords[dim]+shift[dim])/len_l);
    for(int i=-1;i<=1;i++){
        int next_id=center_id+i;
        cur_grid[dim]=next_id;
        double min_coord=next_id*len_l-shift[dim];
        double max_coord=(next_id+1)*len_l-shift[dim];
        double next_dist=0.0;
        if(q_coords[dim]<min_coord){
            next_dist=min_coord-q_coords[dim];
        }
        else if(q_coords[dim]>max_coord){
            next_dist=q_coords[dim]-max_coord;
        }
        dfs(dim+1,cur_dist+next_dist*next_dist);
    }
}
int main(){
    scanf("%d %d %d %d",&d,&n,&m,&r);
    len_l=3.0*r;
    if(len_l<1.0) len_l=1.0;
    mt19937_64 rng(0);
    uniform_real_distribution<double> dist_shift(0.0,len_l);
    for(int i=0;i<d;i++){
        shift[i]=dist_shift(rng);
    }
    for(int i=0;i<n;i++){
        vector<int> tmp(d);
        for(int j=0;j<d;j++){
            scanf("%lf",&points[i][j]);
            tmp[j]=(int)floor((points[i][j]+shift[j])/len_l);
        }
        grid_counts[tmp]++;
    }
    cur_grid.resize(d);
    q_coords.resize(d);
    for(int i=0;i<m;i++){
        for(int j=0;j<d;j++){
            scanf("%lf",&q_coords[j]);
        }
        ans=0;
        dfs(0,0.0);
        printf("%d\n",ans);
    }
}

2026.5.26

排序链表

找到中点然后归并排序。

# Definition for singly-linked list.
from typing import Optional
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
class Solution:
    def middle(self,head):
        slow=fast=head
        while(fast and fast.next):
            pre=slow
            slow=slow.next
            fast=fast.next.next
        pre.next=None
        return slow
    def merge(self,l1,l2):
        cur=dummy=ListNode()
        while(l1 and l2):
            if(l1.val<l2.val):
                cur.next=l1
                l1=l1.next
            else:
                cur.next=l2
                l2=l2.next
            cur=cur.next
        if(l1):
            cur.next=l1
        if(l2):
            cur.next=l2
        return dummy.next
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if(head is None or head.next is None):
            return head
        head2=self.middle(head)
        head=self.sortList(head)
        head2=self.sortList(head2)
        return self.merge(head,head2)

合并 K 个升序链表

小根堆存储每个表头,然后每次取最小的并将其后一个加入堆。注意如果加入自定义类型需要定义__lt__lt,以及Leetcode的自定义节点会覆盖掉自己写的,所以前面加入值和下标以确保不会比较到节点上。

# Definition for singly-linked list.
from typing import List, Optional
import heapq
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next
    def __lt__(self,other):
        return self.val<other.val
class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        h=[]
        n=len(lists)
        for i in range(n):
            if(lists[i]):
                heapq.heappush(h,(lists[i].val,i,lists[i]))
        dummy=cur=ListNode()
        while(h):
            val,index,node=heapq.heappop(h)
            cur.next=node
            cur=cur.next
            if(node.next):
                heapq.heappush(h,(node.next.val,index,node.next))
        return dummy.next

对称二叉树

递归即可.

# Definition for a binary tree node.
from typing import Optional
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def check(p,q):
        if(p is None and q is None):
            return True
        if(p is None or q is None):
            return False
        if(p.val!=q.val):
            return False
        return Solution.check(p.left,q.right) and Solution.check(p.right,q.left)
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        if(root is None):
            return True
        return Solution.check(root.left,root.right)

二叉树的直径

递归将左子树和右子树的深度相加.

# Definition for a binary tree node.
from typing import Optional
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        ans=0
        def dfs(root,dep):
            nonlocal ans
            if(root is None):
                return dep
            left_depth=dfs(root.left,dep+1)
            right_depth=dfs(root.right,dep+1)
            ans=max(ans,left_depth+right_depth-2*dep-2)
            return max(left_depth,right_depth)
        dfs(root,0)
        return ans

验证二叉搜索树

bfs看前一个数是否小于自己即可。或者递归,每次检查对应的区间范围并更新。

# Definition for a binary tree node.
from typing import Optional
from math import inf
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        pre=-inf
        def search(node):
            nonlocal pre
            if(node is None):
                return True
            if(not search(node.left)):
                return False
            if(node.val<=pre):
                return False
            pre=node.val
            return search(node.right)
        return search(root)

二叉树的右视图

层次遍历找每层最右边的。一直搜右边不一定是解。

# Definition for a binary tree node.
from typing import Optional, List
from collections import deque
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        node=root
        ans=[]
        q=deque()
        q.append(node)
        if(node==None):
            return ans
        while(q):
            n=len(q)
            for i in range(n):
                node=q.popleft()
                if(i==n-1):
                    ans.append(node.val)
                if(node.left):
                    q.append(node.left)
                if(node.right):
                    q.append(node.right)
        return ans

二叉树展开为链表

空间O(1)的方法是类似Morris遍历,把左子树叶子接回来。

# Definition for a binary tree node.
from typing import Optional
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def flatten(self, root: Optional[TreeNode]) -> None:
        """
        Do not return anything, modify root in-place instead.
        """
        res=[]
        def dfs(node):
            if(node is None):
                return
            res.append(node)
            dfs(node.left)
            dfs(node.right)
        dfs(root)
        for i in range(len(res)-1):
            res[i].left=None
            res[i].right=res[i+1]

2026.5.27

01最小生成树

要求最小生成树即需要包含尽可能多的零边。考虑零边连接成的连通块,最终的MST即为将这些连通块连在一起,因此MST的大小即为连通块个数减1.使用bfs+差集操作避免超时。

import sys
from collections import deque
sys.setrecursionlimit(10**7)
n,m=map(int,input().split())
g=set()
for i in range(m):
    x,y=map(int,input().split())
    g.add((x,y))
    g.add((y,x))
ans=0
vis=set()
for i in range(1,n+1):
    vis.add(i)
q=deque()
for i in range(1,n+1):
    if(i in vis):
        vis.remove(i)
        q.append(i)
        while(q):
            node=q.popleft()
            tmp=set()
            for j in vis:
                if((node,j) not in g):
                    tmp.add(j)
                    q.append(j)
            vis-=tmp           
        ans+=1
print(ans-1)

邮递员送快递

构建正向与反向图,并分别跑一遍Dijkstra。注意如果使用邻接矩阵存图要判重,以及此处要使用邻接表以避免超时。

import heapq
n,m=map(int,input().split())
a=[[]for _ in range(n+1)]
b=[[]for _ in range(n+1)]
for i in range(m):
    x,y,z=map(int,input().split())
    a[x].append((y,z))
    b[y].append((x,z))
dist=[float("inf") for _ in range(n+1)]
dist[1]=0
dist2=[float("inf") for _ in range(n+1)]
dist2[1]=0
h=[]
heapq.heappush(h,(0,1))
while(h):
    d,node=heapq.heappop(h)
    if(d>dist[node]):
        continue
    for nxt,cost in a[node]:
        if(dist[node]+cost<dist[nxt]):
            dist[nxt]=dist[node]+cost
            heapq.heappush(h,(dist[nxt],nxt))
h=[]
heapq.heappush(h,(0,1))
while(h):
    d,node=heapq.heappop(h)
    if(d>dist2[node]):
        continue
    for nxt,cost in b[node]:
        if(dist2[node]+cost<dist2[nxt]):
            dist2[nxt]=dist2[node]+cost
            heapq.heappush(h,(dist2[nxt],nxt))
ans=0
for i in range(1,n+1):
    ans+=dist[i]+dist2[i]
print(ans)

累加树

插入构建BST,然后从大到小,即右根左遍历,全局变量累加和并更新到节点中。最后bfs层序遍历输出。

from collections import deque
class TreeNode:
    def __init__(self,val=0,left=None,right=None,sum=0):
        self.val=val
        self.left=left
        self.right=right
        self.sum=sum
def insert(root,val):
    if(root is None):
        return TreeNode(val)
    if(val<root.val):
        root.left=insert(root.left,val)
    else:
        root.right=insert(root.right,val)
    return root
n=int(input())
a=list(map(int,input().split()))
root=TreeNode(a[0])
for i in range(1,n):
    insert(root,a[i])
tot=0
def dfs(root):
    global tot
    if(root is None):
        return 0
    dfs(root.right)
    tot+=root.val
    root.sum=tot
    dfs(root.left)
dfs(root)
q=deque()
q.append(root)
ans=[]
while(q):
    node=q.popleft()
    ans.append(node.sum)
    if(node.left):
        q.append(node.left)
    if(node.right):
        q.append(node.right)
print(*ans)

火星大工程

from collections import deque
n,m=map(int,input().split())
g=[[]for _ in range(n+1)]
r=[[]for _ in range(n+1)]
in_deg=[0]*(n+1)
out_deg=[0]*(n+1)
for i in range(m):
    x,y,z=map(int,input().split())
    g[x].append((y,z))
    r[y].append((x,z))
    in_deg[y]+=1
    out_deg[x]+=1
q=deque()
ve=[0]*(n+1)
for i in range(1,n+1):
    if(in_deg[i]==0):
        q.append(i)
while(q):
    node=q.popleft()
    for i,val in g[node]:
        ve[i]=max(ve[i],ve[node]+val)
        in_deg[i]-=1
        if(in_deg[i]==0):
            q.append(i)

ans=max(ve)
print(ans)
vl=[ans]*(n+1)
q=deque()
for i in range(1,n+1):
    if(out_deg[i]==0):
        q.append(i)
while(q):
    node=q.popleft()
    for i,val in r[node]:
        vl[i]=min(vl[i],vl[node]-val)
        out_deg[i]-=1
        if(out_deg[i]==0):
            q.append(i)
res=[]
for i in range(1,n+1):
    for j,k in g[i]:
        if(ve[i]+k==vl[j]):
            res.append((i,j))
res.sort()
for i,j in res:
    print(i,j)

upstairs

同余最短路。选择a,b,c中最小的数(不妨a)作为最后叠加的层数,问题转化为在模a意义下,h=by+cz,而当此时的h最小,它是否小于目标的h。因此对每个余数u构建(u+b) mod a和(u+c) mod a的路径,从0开始跑一遍Dijkstra,得到的即为最小的h.

import heapq
a,b,c=map(int,input().split())
query=int(input())
step=[]
if(a>0):
    step.append(a)
if(b>0):
    step.append(b)
if(c>0):
    step.append(c)
step.sort()
if(len(step)>=2):
    m=step[0]
    edges=step[1:]
    dist=[float("inf")]*m
    dist[0]=0
    q=[]
    heapq.heappush(q,(0,0))
    while(q):
        d,node=heapq.heappop(q)
        if(d>dist[node]):
            continue
        for i in edges:
            nxt=(node+i)%m
            if(dist[nxt]>d+i):
                dist[nxt]=d+i
                heapq.heappush(q,(dist[nxt],nxt))
def solve(h):
    if(len(step)==0):
        if(h==0):
            return True
        else:
            return False
    elif(len(step)==1):
        if(h%step[0]==0):
            return True
        else:
            return False
    rem=h%m
    if(dist[rem]<=h):
        return True
    else:
        return False
for i in range(query):
    h=int(input())
    if(solve(h)):
        print("Yes")
    else:
        print("No")

猫猫搭积木

按秩合并并查集,同时维护代表节点的集合中的所有元素。注意计算集合个数的操作需要特判自己与自己合并的情况,因此在merge函数中进行。

n,q,s=map(int,input().split())
fa=[i for i in range(n+1)]
siz=[1]*(n+1)
group=[set([i]) for i in range(n+1)]
ans=n
def find(x):
    if(fa[x]==x):
        return x
    fa[x]=find(fa[x])
    return fa[x]
def merge(x,y):
    global ans
    fx=find(x)
    fy=find(y)
    if(fx==fy):
        return
    if(siz[fx]<siz[fy]):
        fx,fy=fy,fx
    fa[fy]=fx
    siz[fx]+=siz[fy]
    group[fx]|=group[fy]
    ans-=1
for i in range(q):
    x,y=map(int,input().split())
    merge(x,y)
    fx=find(x)
    if(siz[fx]>=s):
        ans+=siz[fx]-1
        for j in group[fx]:
            if(j==fx):
                continue
            fa[j]=j
            siz[j]=1
            group[j]=set([j])
        fa[fx]=fx
        siz[fx]=1
        group[fx]=set([fx])
    print(ans)
    # for j in range(1,n+1):
    #     print(find(j),end=" ")

从前序与中序遍历序列构造二叉树

# Definition for a binary tree node.
from typing import List, Optional
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
        if not preorder or not inorder:
            return None
        root_val=preorder[0]
        root=TreeNode(root_val)
        root_index=inorder.index(root_val)
        root.left=self.buildTree(preorder[1:1+root_index],inorder[:root_index])
        root.right=self.buildTree(preorder[1+root_index:],inorder[root_index+1:])
        return root

路径总和 III

dfs->前缀和。 使用哈希表存储前缀和出现的次数,遍历到每一个都判断是否可以对答案造成贡献。

# Definition for a binary tree node.
from typing import Optional
from collections import defaultdict
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
        cnt=defaultdict(int)
        cnt[0]=1
        ans=0
        def dfs(node,cur_sum):
            nonlocal ans
            if(not node):
                return
            cur_sum+=node.val
            ans+=cnt[cur_sum-targetSum]
            cnt[cur_sum]+=1
            dfs(node.left,cur_sum)
            dfs(node.right,cur_sum)
            cnt[cur_sum]-=1
        dfs(root,0)
        return ans

二叉树中的最大路径和

递归求解每个点的贡献(自己的值+左子树贡献+右子树贡献)。然后经过每个点的路径最大值即为自己加上左右子树的最大贡献。

# Definition for a binary tree node.
from typing import Optional
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        maxm=-float('inf')
        def maxgain(node):#以node为根,相当于node的贡献
            nonlocal maxm
            if(node is None):
                return 0
            left_gain=max(maxgain(node.left),0)
            right_gain=max(maxgain(node.right),0)
            maxm=max(maxm,node.val+left_gain+right_gain)
            return node.val+max(left_gain,right_gain)
        maxgain(root)
        return maxm

终于把力扣100写完了! image image

2026.5.30

最小二乘法

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<random>
using namespace std;
#pragma GCC optimize("O2")
int n,d;
int m=8000;
int main(){
    scanf("%d %d",&n,&d);
    mt19937 rng(0);
    uniform_int_distribution<int> dist_row(0,m-1);
    uniform_int_distribution<int> dist_sign(0,1);
    vector<vector<double>> hatx(m,vector<double>(d,0.0));
    vector<double> haty(m,0.0);
    for(int i=0;i<n;i++){
        int l;
        scanf("%d",&l);
        int h=dist_row(rng);
        int s=dist_sign(rng)*2-1;
        vector<pair<int,double>> v(l);
        for(int j=0;j<l;j++){
            scanf("%d %lf",&v[j].first,&v[j].second);
            v[j].first--;
        }
        double y;
        scanf("%lf",&y);
        for(int j=0;j<l;j++){
            hatx[h][v[j].first]+=s*v[j].second;
        }
        haty[h]+=s*y;
    }
    //A=hatx^T *hatx B=hatx^T *haty
    vector<vector<double>> A(d,vector<double>(d,0.0));
    vector<double> B(d,0.0);
    for(int i=0;i<m;i++){
        for(int j=0;j<d;j++){
            double val=hatx[i][j];
            for(int k=j;k<d;k++){
                A[j][k]+=val*hatx[i][k];
            }
            B[j]+=val*haty[i];
        }
    }
    // for(int j=0;j<d;j++){
    //     A[j][j]+=1e-9;
    // }
    for(int i=0;i<d;i++){
        for(int j=0;j<i;j++){
            A[i][j]=A[j][i];
        }
    }
    for(int i=0;i<d;i++){
        int pivot=i;
        for(int j=i+1;j<d;j++){
            if(abs(A[j][i])>abs(A[pivot][i])){
                pivot=j;
            }
        }
        if(pivot!=i){
            swap(A[i],A[pivot]);
            swap(B[i],B[pivot]);
        }
        if(abs(A[i][i])<1e-12){
            continue;
        }
        for(int j=i+1;j<d;j++){
            double factor=A[j][i]/A[i][i];
            for(int k=i;k<d;k++){
                A[j][k]-=factor*A[i][k];
            }
            B[j]-=factor*B[i];
        }
    }
    vector<double> w(d,0.0);
    for(int i=d-1;i>=0;i--){
        double sum=B[i];
        for(int j=i+1;j<d;j++){
            sum-=A[i][j]*w[j];
        }
        if(abs(A[i][i])<1e-12){
            w[i]=0.0;
        }else{
            w[i]=sum/A[i][i];
        }
    }
    for(int i=0;i<d;i++){
        printf("%lf ",w[i]);
    }
}

最大主成分

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<random>
#include<cstdlib>
using namespace std;
#pragma GCC optimize("O2")
struct Element{
    int i,j;
    long double val;
};
int n,m,d;
int main(){
    scanf("%d %d %d",&n,&d,&m);
    vector<Element> a(m);
    for(int i=0;i<m;i++){
        scanf("%d %d %Lf",&a[i].i,&a[i].j,&a[i].val);
        a[i].i--;
        a[i].j--;
    }
    vector<long double> x(d,1.0);
    long double sum=0;
    for(int j=0;j<d;j++){
        sum+=x[j]*x[j];
    }
    sum=sqrt(sum);
    for(int j=0;j<d;j++){
        x[j]/=sum;
    }
    int iter=100;
    vector<long double> y(n,0.0);
    for(int i=0;i<iter;i++){
        vector<long double> y(n,0.0);
        for(int j=0;j<m;j++){
            y[a[j].i]+=a[j].val*x[a[j].j];
        }
        vector<long double> z(d,0.0);
        for(int i=0;i<m;i++){
            z[a[i].j]+=a[i].val*y[a[i].i];
        }
        long double norm=0;
        for(int j=0;j<d;j++){
            norm+=z[j]*z[j];
        }
        norm=sqrt(norm);
        for(int j=0;j<d;j++){
            x[j]=z[j]/norm;
        }
    }
    for(int i=0;i<d;i++){
        printf("%Lf\n",x[i]);
    }
    return 0;
}

DISCUSSION

Comments

Sign in with GitHub to join the conversation.