" 快要考CSP了 "
今年CSP没进TG(四川省喝了假酒
大概只能再骗一次普及了
整理一下算法模板吧
首先是较为简单的一些数论模板
EXGCD
#include<iostream> using namespace std; int X,Y; int x1,y1; int gcd(int a,int b) { return b ? gcd(b,a%b) : a; } int exgcd(int a,int b,int &x1,int &y1) //附带求gcd的功能 { if(b==0){ x=1; //当 ax+by=gcd(a,b)中b=0时,a=gcd(a,b),x=1,y=0 y=0; return a; //gcd功能实现,b=0时gcd(a,b)=a } int x2,y2; int t=exgcd(b,a%b,x2,y2); x1=y2; y1=x2-(a/b)*y2; return t; //返回gcd(a,b) } int main() { int a,b,c; cin>>a>>b>>c; int l=c/exgcd(a,b,x1,y1); X=x1*l; Y=y1*l; cout<<X<<" "<<Y<<endl; }
埃氏筛
void work_prime(int n) { for(int i=4;i<=n;i+=2){ f[i]=true; } for(int i=3;i<=n;i++){ if(!f[i]){ for(int j=i*2;j<=n;j+=i){ f[j]=true; } } } }
不必过多赘述
接下来是一些基础图论算法的模板
二分图与匈牙利
#include<bits/stdc++.h> using namespace std; int ans=0; const int maxn=1007; bool vis[maxn]; int n,m; int e; bool g[maxn][maxn]; int match[maxn]; bool ap(int x){ for(int j=1;j<=m;++j){//扫描每一个点 if(g[x][j]&&!vis[j]){//如果x,j之间存在一条边且j未被访问过 vis[j]=true; //将j点标记为过 if(!match[j]||ap(match[j])){ //如果这是增广路的中点或还能从这个点找到增广路 match[j]=x; //变反 return true;//返回能找到 } } } return false; //如果扫描了每一个点都无法找到增广路就说明找不到增广路了 } void work() { for(int i=1;i<=n;++i){ //枚举增广路起始点 memset(vis,0,sizeof(vis)); if(ap(i)){ ans++; } } return ; } int main() { cin>>n>>m>>e; for(int i=1;i<=e;i++){ int u,v; cin>>u>>v; g[u][v]=true; } work(); cout<<ans; }
SPFA
#include <iostream> #include <stdio.h> #include <queue> #include <vector> #include <math.h> using namespace std; const int inf = 2147483647; const int maxn = 10007; const int maxm = 500007; int dist[maxn],inQ[maxn]; vector<int> G[maxn]; //G[i][j] 代表以i点为起始点指向另一个点,的第j条边的编号 queue<int> Q; //记录点的队列,用于一遍遍扫描整张图 int m,n,S; int U[maxm],V[maxm],C[maxm]; //编号为i的边的左端点,右端点,权值 void spfa(int s) { for(int i=1;i<=m+1;++i) { dist[i]=inf; } dist[s] = 0; Q.push(s); while(!Q.empty()) { int u=Q.front();Q.pop(); //查找队头的点并将其取出 // cout<<"u:"<<u<<" "; inQ[u]=0; //将其状态赋为不在队列中 for(int i=0;i<G[u].size();++i) { int e=G[u][i]; //cout<<"e:"<<e<<" "; int v=V[e]^U[e]^u; //其实可以直接用V[e]但这里以防万一(吃饱了撑着)还是这样打 if(dist[u]+C[e]<dist[v]) { dist[v]=dist[u]+C[e]; if(!inQ[v]){ Q.push(v); inQ[v]=1; } } } //cout<<endl; } return ; } int main() { cin>>m>>n>>S; for(int i=1;i<=n;i++){ int u,v,c; cin>>u>>v>>c; G[u].push_back(i); U[i]=u; V[i]=v; C[i]=c; } spfa(S); for(int i=1;i<=m;i++){ cout<<dist[i]<<" "; } return 0; }
kruskal
#include<bits/stdc++.h> const int MAX=1000007; using namespace std; int m,n; long long ans=0; struct edge { int val; int l,r; } e[MAX]; bool com(edge a,edge b) { return a.val<b.val; } int fa[5001]; void init(int x) { for(;x>=1;x--) fa[x]=x; } int find(int x) { if(x==fa[x]) return x; int k=find(fa[x]); fa[x]=k; return k; } bool merge(int x,int y) { int xx=find(x); int yy=find(y); if(xx==yy) return false; fa[xx]=yy; return true; } int main() { cin>>n>>m; init(n); for(int i=1;i<=m;++i){ cin>>e[i].l>>e[i].r>>e[i].val; } sort(e+1,e+1+m,com); for(int i=1;i<=m;i++){ if(merge(e[i].l,e[i].r)){ ans+=e[i].val; } } for(int i=2;i<=n;i++){ if(find(i)!=find(i-1)){ cout<<"orz"; return 0; } } cout<<ans; }
dinic与网络最大流
#include<bits/stdc++.h> using namespace std; int INF=1e9; const int maxn=100007; const int maxm=100007; int l[maxn]; //记录当前点在增广路中的深度为深搜提供便利 int head[maxn]; //head[u]记录u点的头指针 struct edge { int to; //指向的下一个点的编号 int cost; //边权 int next; //指向的同一起始点的下一条边的编号 方便广搜遍历 } es[maxm*2]; //记录边以及它的反弧 int cnt=0; //记录边的个数即其编号 void init(){ for(int i=1;i<=maxn*2;i++) head[i]=-1; } void addedge(int u,int v,int cost) //添加边以及其反弧 { es[cnt].to=v; es[cnt].cost=cost; es[cnt].next=head[u];//添加边 /* 1.将新边的头指针指向原来U点的头指针 2.将原来U点的头指针指向新边 3.并将新边的边权赋为cost 这三步操作即将此边放在链表的首位 */ head[u]=cnt; ++cnt; es[cnt].to=u; es[cnt].cost=0; es[cnt].next=head[v]; //添加反弧 /* 进行与正边相反的操作 并将反弧的初始权值赋为0 */ head[v]=cnt; ++cnt; } bool ap(int s,int t) //搜索是否能找到增广路 { memset(l,0,sizeof(l)); l[s]=1; queue<int> q; //利用队列存点更加便捷 q.push(s); while(!q.empty()) //广搜过程不需解释 { int u=q.front(); q.pop(); if(u==t) //如果搜索到了汇点 return true; for(int v=head[u];v!=-1;v=es[v].next){ int k=es[v].to; //记录当前遍历到的下一个点 if(!l[k]&&es[v].cost){ //如果此点未被遍历且连接两个点的两条边权不为零 l[k]=l[u]+1; q.push(k); } } } return false; //没有找到 } int dfs(int x,int t,int ans) //x为当前遍历到的点,t为汇点,ans为增广路的增益也是流量的限制 { if(x==t){ return ans; } int sum=0; for(int i=head[x];i!=-1;i=es[i].next){ if(es[i].cost&&l[x]==l[es[i].to]-1){ //如果改边流量没有用完且下一个点在增广路上的下一个点 int f=dfs(es[i].to,t,min(es[i].cost,ans-sum)); es[i].cost-=f; es[i^1].cost+=f; //反弧加上相应值 sum+=f; if(sum==ans) return sum; } } return sum; } int dinic(int s,int t) //s为源点,t为汇点 { int ans=0; while(ap(s,t)) //重复找s到t的增广路 { ans+=dfs(s,t,INF); } return ans; } int N,M,S,T; int main() { init(); cin>>N>>M>>S>>T; for(int i=1;i<=M;i++){ int aa,bb,cc; cin>>aa>>bb>>cc; addedge(aa,bb,cc); } int ans=dinic(S,T); cout<<ans; return 0; }
最后是字符串算法
KMP
#include<iostream> using namespace std; const int maxn=1000007; char st[maxn]; //定义模式串 char s[maxn]; int fail[maxn]; int siz=1; void init()//初始化fail数组 { fail[0]=0; //根据定义,fail[0]=0 for(int i=1,j=0;st[i];i++){ //i是迭代变量,j存储fail[i-1] while(j&&st[i]!=st[j]){ //搜索合适的Border j=fail[j-1]; } if(st[i]==st[j]){ //找到Border(i) fail[i]=++j; } siz++; } } int search(char *str) //KMP算法的主程序(搜索匹配)部分,str为主串 { int ans=0; for(int i=0,j=0;str[i];i++){ //i为主串指针,j为模式串指针 while(j&&str[i]!=st[j]) //搜索合适的Border j=fail[j-1]; if(str[i]==st[j]){ if(!st[++j]){ ans++; } } } return ans; } int main() { cin>>s; cin>>st; init(); int ans=search(s); //search函数的返回值为出现的次数 cout<<ans<<endl; }