整理的小技巧
一种很有意思的GCD写法(代码为xuziyang大佬所写)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | long long gcd( long long a, long long b){ if (a==0) return b; if (b==0) return a; if (!(a&1)&&!(b&1)) //判断“如果a、b都为偶数”。&1表示按位与二进制的00……001,还因为奇数的二进制为1,偶数的二进制为0,所以奇数它就会返回1,偶数就返回0;再取个!非符号,偶数就变成1 return 2*gcd(a>>1,b>>1); //a/2等价于a>>1 else if (!(a&1)) //判断是不是只有a为偶数 return gcd(a>>1,b); else if (!(b&1)) //判断是不是只有b为偶数 return gcd(a,b>>1); else //剩下的情况就是两个都为奇数 return gcd(abs(a-b),min(a,b)); } |
johnson算法
#include<cstdio> #include<queue> #define MAXN 5005 #define MAXM 10005 #define INF 1e9 using namespace std; int n,m; int vis[MAXN]; long long h[MAXN],dis[MAXN]; bool f[MAXN]; struct graph { int tot; int hd[MAXN]; int nxt[MAXM],to[MAXM],dt[MAXM]; void add(int x,int y,int w) { tot++; nxt[tot]=hd[x]; hd[x]=tot; to[tot]=y; dt[tot]=w; return ; } }g;//链式前向星 bool SPFA(int s)//这里用了Bellman-Ford的队列优化 { queue<int>q; for(int i=1;i<=n;++i) h[i]=INF,f[i]=false; h[s]=0; f[s]=true; q.push(s); while(!q.empty()) { int xx=q.front(); q.pop(); f[xx]=false; for(int i=g.hd[xx];i;i=g.nxt[i]) if(h[g.to[i]]>h[xx]+g.dt[i]) { h[g.to[i]]=h[xx]+g.dt[i]; if(!f[g.to[i]]) { if(++vis[g.to[i]]>=n) return false;//注意在有重边的情况下要记录入队次数而不是松弛次数 f[g.to[i]]=true,q.push(g.to[i]); } } } return true; } void dijkstra(int s) { priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q; for(int i=1;i<=n;i++) dis[i]=INF,f[i]=false; q.push(make_pair(0,s)); dis[s]=0; while(!q.empty()) { int xx=q.top().second; q.pop(); if(!f[xx]) { f[xx]=true; for(int i=g.hd[xx];i;i=g.nxt[i]) if(dis[g.to[i]]>dis[xx]+g.dt[i]) { dis[g.to[i]]=dis[xx]+g.dt[i]; if(!f[g.to[i]]) q.push(make_pair(dis[g.to[i]],g.to[i])); } } } return ; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); g.add(u,v,w); } for(int i=1;i<=n;i++) g.add(0,i,0);//建虚拟节点0并且往其他的点都连一条边权为0的边 if(!SPFA(0))//求h的同时也判了负环 { printf("-1"); return 0; } for(int u=1;u<=n;u++) for(int i=g.hd[u];i;i=g.nxt[i]) g.dt[i]+=h[u]-h[g.to[i]];//求新边的边权 for(int i=1;i<=n;i++) { dijkstra(i);//以每个点为源点做一遍dijkstra long long ans=0; for(int j=1;j<=n;j++)//记录答案 if(dis[j]==INF) ans+=1ll*j*INF; else ans+=1ll*j*(dis[j]+(h[j]-h[i])); printf("%lld\n",ans); } return 0; }
并查集两个优化同时使用
#include<stdio.h> #include<stdlib.h> #define MAXN 5005 int fa[MAXN]; int rank[MAXN]; void init(int n) { for (int i = 0; i < n; ++i) { fa[i] = i; rank[i] = 1; } } int f_root(int x) { if (fa[x] == x) return x; else { fa[x]=f_root(fa[x]); return fa[x]; } } void merge(int i, int j) { int x = f_root(i), y = f_root(j); if (rank[x] <= rank[y]) { fa[x] = y; } else fa[y] = x; if (rank[x] == rank[y]&& x!=y) { rank[y]++; } } int main() { int n, m, p, x, y; scanf("%d%d%d", &n, &m, &p); init(n); for (int i = 0; i < m ;++i) { scanf("%d%d", &x, &y); merge(x, y); } for (int i = 0; i < p; ++i) { scanf("%d%d", &x, &y); printf("%s\n", f_root(x) == f_root(y) ? "Yes":"No"); } return 0; }
点分治模板
#include <cstdio> #include <algorithm> #define Max 20010 #define add(u,v,w) (To[++num]=Head[u],Head[u]=num,V[num]=v,W[num]=w) #define For(x) for (int h=Head[x],o=V[h]; h; h=To[h],o=V[h]) #define Input for (int i=1,u,v,w; i<N; i++) scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w) using namespace std; int N,K,root,num,ans,cnt,mins,Head[Max],To[Max],V[Max],W[Max],siz[Max],dis[Max],f[Max]; /* 程序中部分变量说明 dis[i] 所有路径到 重心 的长度 siz[i] 以 i 为根节点的子树的大小(当前重心下) f[i] i 是否还存在(分治完一次后就把重心删掉了) cnt 记录 dis 的个数(即路径个数) root 当前子树的重心 maxs 当前讨论的点所有子树大小中最大值(并不是全局变量,是尝试每个重心时重新开的一个变量) mins 讨论过的点的子树大小中最大的最小值(是全局变量,用来确定哪个才是重心) */ int get_size(int x,int fa){ //返回以 x 为根的子树大小,其中 x 父节点为 fa siz[x]=1; for(int i=Head[x];i;i=To[i]){ int vt=V[i]; if(vt==fa){ continue; } if(f[vt]){ continue; } siz[x]+=gs(vt,x); } return siz[x]; } void get_dis(int x,int d,int fa){ //x 到重心的长度为 d,之后继续 dfs dis[++cnt]=d; For(x) if (o!=fa && !f[o]) get_dis(o,d+W[h],x); return; } void dfs_root(int x,int tot,int fa) { //求目标子树的重心(要求除去 x 点时,它的 maxs 值最小,那么 x 就是这棵子树的重心了),其中 tot 是这棵子树的总大小(节点个数) int maxs=tot-siz[x]; //这棵子树中x 父亲那一支先赋给 maxs For(x) if (o!=fa && !f[o]){ maxs=max(maxs,siz[o]); dfs_root(o,tot,x); } if (maxs<mins){ mins=maxs; root=x; } return; } int work(int x,int d) { //返回以 x 为根的子树内长度小于等于 K 的路径数(两个端点都在子树内) //其实 d 在这里用处只有一个,是在做减法时方便把重心的儿子节点的 dis 先弄好,你也可以在分治的时候弄,不过就稍微有点麻烦了 cnt=0; get_dis(x,d,0); sort(dis+1,dis+cnt+1); int daan=0,i=1,j=cnt; while (i<j){ while (i<j && dis[i]+dis[j]>K) j--; daan+=j-i; //相当于选一条路径 i,另一条可以为 [i+1,j] 里任意一条路径,这样得到的两个点之间长度(经过重心的那条路径)肯定是小于等于 K 的 i++; } return daan; } void dfs(int x){ //以 x 为重心分治一下 cnt=0; mins=Max; get_size(x,0); dfs_root(x,siz[x],0); ans+=work(root,0); f[root]=1; For(root) if (!f[o]){ //注意这里是以重心开始 ans-=work(o,W[h]); //注意,这里 dis[o] 要先赋成 W[h](即它到重心的距离) dfs(o); } return; } int main(){ while(scanf("%d%d",&N,&K)!=EOF && N && K){ Input; dfs(1); printf("%d\n",ans); num=ans=0; for (int i=1; i<=N; i++) Head[i]=f[i]=dis[i]=0; } return 0; }
后缀数组模板标程+资料
#include<bits/stdc++.h> #define N 1000050 using namespace std; char s[N]; int rank[N],y[N],tmp[N]; int c[N],SA[N],n,m,Height[N]; void Sort(){ for(int i=1;i<=m;i++) c[i] = 0; for(int i=1;i<=n;i++) c[rank[i]]++; for(int i=2;i<=m;i++) c[i] += c[i-1]; for(int i=n;i>=1;i--) SA[c[rank[y[i]]]--] = y[i]; } void get_SA(){ for(int i=1;i<=n;i++) rank[i] = s[i] , y[i] = i; Sort(); for(int k=1;k<=n;k<<=1){ int cnt = 0; for(int i=n-k+1;i<=n;i++) y[++cnt] = i; for(int i=1;i<=n;i++) if(SA[i]>k) y[++cnt] = SA[i] - k; Sort(); swap(rank,tmp); int num = 1; rank[SA[1]] = 1; for(int i=2;i<=n;i++){ if(tmp[SA[i]] == tmp[SA[i-1]] && tmp[SA[i]+k] == tmp[SA[i-1]+k]) rank[SA[i]] = num; else rank[SA[i]] = ++num; } m = num; } } void get_Hi(){ int k = 0; for(int i=1;i<=n;i++){ if(rank[i]==1) continue; int j = SA[rank[i]-1]; if(k) k--; while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++; Height[rank[i]] = k; } } int main(){ scanf("%s",s+1); n = strlen(s+1); m = 127; get_SA(); get_Hi(); for(int i=1;i<=n;i++) printf("%d ",SA[i]); return 0; } //正常代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】