常用模板总结(持续更新)
【gcd】
int gcd(int n,int m){ if(n%m==0) return m; else return gcd(m,n%m); }
【快速幂】
typedef long long ll; ll pow_mod(ll x,ll n ,ll mod ){ ll res=1; x=x%mod; while(n){ if(n%2) res=res*x%mod; x=x*x%mod; n/=2; } return res; }
【图论算法】
-
【SPFA】
#include<iostream> #include<vector> #include<queue> #include<cstdio> #define INF 99999999 using namespace std; const int maxn = 222; int mapp[maxn][maxn],n,m; int vis[maxn], dis[maxn]; int SPFA(int st,int end){ for(int i=0;i<n;i++){ // 初始化 dis[i] = INF; vis[i] = 0;//G[i].clear(); } queue<int>Q; dis[st] = 0; vis[st] = 1; //起始点距离为0, 标记上 Q.push(st); while(!Q.empty()){ int vex = Q.front(); Q.pop(); vis[vex] = 0; // for(int i=0;i<n;i++){ if( dis[i] > mapp[vex][i] + dis[vex]){ dis[i] = mapp[vex][i] + dis[vex]; if(!vis[i]){ vis[i] = 1; Q.push(i); } } } } return dis[end]; } int main() { while(scanf("%d%d",&n,&m)!=EOF){ for(int i=0;i<n;i++) for(int j=0;j<n;j++) mapp[i][j] = INF; int a,b,c; for(int i=0;i<m;i++){ scanf("%d%d%d",&a,&b,&c); if(c < mapp[a][b]) mapp[a][b] = mapp[b][a] = c; } int s,e; scanf("%d%d",&s,&e); int ans = SPFA(s,e); if(ans < INF) printf("%d\n",ans); else printf("-1\n"); } }
-
【Dijkstra】
#include<iostream> #include<vector> #include<queue> #include<cstdio> #include<cstring> #define INF 0xfffffff using namespace std; const int maxn = 222; int n,m; int map[maxn][maxn]; int dis[maxn]; bool vis[maxn]; int dijkstra(int st,int end){ memset(vis,0,sizeof(vis)); vis[st] = 1; for(int i=0;i<n;i++){ dis[i] = map[st][i]; } dis[st] = 0; for(int num = 1;num<n;num++){ //找到其余的n-1个点 int tmp = INF, k; for(int i=0;i<n;i++){ if(!vis[i] && tmp > dis[i]){ tmp = dis[i]; k = i; } } vis[k] = true; if(tmp == INF) break; // 当前没有找到最短的节点 for(int i=0;i<n;i++) if(!vis[i] && dis[i] > dis[k] + map[k][i] ) dis[i] = dis[k] + map[k][i]; } return dis[end]; } int main(){ while(scanf("%d%d",&n,&m)!=EOF){ for(int i=0;i<n;i++) for(int j=0;j<n;j++) map[i][j] = (i == j)? 0:INF; int a,b,c; for(int i=0;i<m;i++){ scanf("%d%d%d",&a,&b,&c); if(c< map[a][b]) map[a][b] = map[b][a] = c; } int s,e; scanf("%d%d",&s,&e); int ans = dijkstra(s,e); if(ans == INF) printf("-1\n"); else printf("%d\n",ans); } return 0; }
-
【Floyd-Warshall算法】
#include<iostream> #include<vector> #include<queue> #include<cstdio> #define INF 0xfffffff using namespace std; const int maxn = 222; int n,m; int map[maxn][maxn]; void floyed(){ //k ,i ,j for(int k=0;k<n;k++) for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(map[i][j] > map[i][k] + map[k][j]) map[i][j] = map[i][k] + map[k][j]; } int main() { while(scanf("%d%d",&n,&m)!=EOF){ for(int i=0;i<n;i++) for(int j=0;j<n;j++) if(i == j) map[i][j] = 0; else map[i][j] = INF; int a,b,c; for(int i=0;i<m;i++){ scanf("%d%d%d",&a,&b,&c); if(c < map[a][b]) //重边 map[a][b] = map[b][a] = c; } int s,e; scanf("%d%d",&s,&e); floyed(); if(map[s][e] == INF) printf("-1\n"); else printf("%d\n",map[s][e]); } return 0; }
-
【Kruskal算法 最小生成树】
struct node{ int v1,v2,len; }edge[maxn]; void init(){ for(int i=1;i<=n;i++){ father[i] = i; } } int Find(int x){ int r= x; while(r != father[r]) r = father[r]; while(x != r){ int tmp = father[x]; father[x] = r; x = tmp; } return r; } int Find(int x){ if(x == father[x]) return x; else return father[x] = Find(father[x]); } void Union(int x,int y){ int xr = Find(x); int yr = Find(y); if(xr == yr) return; else father[xr] = yr; } void Kruskal(){ // Kruskal 算法 int edgenum=0; for(int i=0;i<len&& edgenum!=n-1;i++){ if(Find(edge[i].v1)!=Find(edge[i].v2)){ ans += edge[i].len; Union(edge[i].v1,edge[i].v2); edgenum++; } } }
-
【PRIM 最小生成树】
注意,不能判环
#define INF 0xfffffff struct node{ int v,len; node(int v=0, int len = 0):v(v),len(len){} }; vector <node> G[maxn]; int intree[maxn]; int minDist[maxn]; void init(){ for(int i=0;i<maxn;i++){ intree[i]=0; G[i].clear(); minDist[i]=INF; } } int prim(int s){ intree[s]=1; int ans=0; for(int i=0;i<G[s].size();i++){ int vex = G[s][i].v; minDist[vex] = min(G[s][i].len,minDist[vex]); //判重边 } for(int nodeNum=0;nodeNum<n-1;nodeNum++){ int tmpMin=INF; int addNode; for(int i=1;i<=n;i++){ //从1 到 n 的城市标号 if(!intree[i]&&minDist[i]<tmpMin){ tmpMin=minDist[i]; addNode = i; } } if(tmpMin==INF) { return -1; } ans+=tmpMin; intree[addNode]=1; for(int i=0;i<G[addNode].size();i++){ int vex = G[addNode][i].v; if(!intree[vex]&&G[addNode][i].len<minDist[vex]) minDist[vex] = G[addNode][i].len; } } return ans; }
-
【匈牙利算法】二分图最大匹配
int findpath(int k){ for(int i=head[k];i!=-1;i=edge[i].next){ int v = edge[i].to; if(!inpath[v]){ inpath[v]=1; if(match[v]==-1||findpath(match[v])){ match[v]=k;return true; } } } return false; } void hungary(){ int ans=0; for(int i=1;i<=n;i++){ memset(inpath,0,sizeof(inpath)); if(findpath(i)){ //寻找增广路 ans++; } } cout<<ans<<endl; } void init(){ memset(head,-1,sizeof(head)); memset(match,-1,sizeof(match)); edgeNum=0; }
数据结构
-
【线段树点更新,区间查询】
#define L(m) m<<1 #define R(m) m<<1|1 const int maxn = 1000000+1; int num[maxn]; struct node{ int l,r,sum; }tree[maxn<<2]; //开4倍数组 void Build(int m,int l,int r){ tree[m].l=l; tree[m].r=r; //赋初值 if(tree[m].l==tree[m].r) { tree[m].sum=num[l]; return ;//不要忘了return } int mid = (tree[m].l+tree[m].r)>>1; Build(L(m),l,mid); //递归构造左右子树 Build(R(m),mid+1,r); tree[m].sum = tree[L(m)].sum+tree[R(m)].sum; //回溯,将子节点的sum加到父节点上 } void Update(int m,int a,int x){ if(tree[m].l==a && tree[m].r==a){ tree[m].sum+=x; return ; //这个return 忘了写找了好久的错 } int mid = (tree[m].l+tree[m].r)>>1; if(mid>=a) Update(L(m),a,x); else Update(R(m),a,x); tree[m].sum=tree[L(m)].sum+tree[R(m)].sum; } int Query(int m,int l,int r){ if(tree[m].l==l && tree[m].r==r){ return tree[m].sum; } int mid = (tree[m].l+tree[m].r)>>1; if(mid>=r) //这里也可以写成 if else if else return Query(L(m),l,r); if(mid<l) return Query(R(m),l,r); return Query(L(m),l,mid)+Query(R(m),mid+1,r); }
-
【线段树区间修改,点查询】
#define L(m) m<<1 #define R(m) m<<1|1 using namespace std; const int maxn =100000+10; typedef long long ll; ll num[maxn]; struct node{ ll l,r,sum; ll add; }tree[maxn<<2]; void pushup(ll m){ tree[m].sum = tree[L(m)].sum + tree[R(m)].sum; } void pushdown(ll m){ if(tree[m].add){ ll tmp = tree[m].add; tree[L(m)].sum += (tree[L(m)].r-tree[L(m)].l+1)*tmp; tree[R(m)].sum += (tree[R(m)].r-tree[R(m)].l+1)*tmp; tree[L(m)].add +=tmp; //所有的都是 += ; tree[R(m)].add +=tmp; tree[m].add = 0; } } void Update(ll m,ll l,ll r,ll x){ if(tree[m].l >=l && tree[m].r <=r){ tree[m].add += x; //注意这里是+= 不是 = tree[m].sum += (tree[m].r - tree[m].l + 1) *x; return ; } pushdown(m); ll mid = (tree[m].l + tree[m].r)>>1; //这里取中间取得是当前节点左右区间的中点 if(mid>=r) 如果要更新的区间在左边 Update(L(m),l,r,x); else if(mid<l) Update(R(m),l,r,x); else{ Update(L(m),l,mid,x); Update(R(m),mid+1,r,x); } pushup(m); } ll Query(ll m,ll l,ll r){ if(tree[m].l==l && tree[m].r==r){ return tree[m].sum; } pushdown(m); ll mid = (tree[m].l+ tree[m].r)>>1; if(mid>=r) return Query(L(m),l,r); if(mid<l) return Query(R(m),l,r); //这里也可以写成 if else else if return Query(L(m),l,mid)+Query(R(m),mid+1,r); } void Build(ll m,ll l,ll r){ tree[m].l = l,tree[m].r = r; tree[m].add=0; //不要忘了初始化 if(l == r){ tree[m].add = 0; tree[m].sum = num[l]; return ; } int mid = (l + r)>>1; Build(L(m),l,mid); Build(R(m),mid+1,r); pushup(m); }
-
【线段树模板2,单点更新】
#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;
#define maxn 50005
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int sum[maxn<<2];
void Pushup(int rt){
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void Build(int l,int r,int rt){
if(l == r){
scanf("%d",&sum[rt]);
}
else{
int m = (l + r)>>1;
Build(lson);
Build(rson);
Pushup(rt);
}
}
void Update(int p,int add,int l,int r,int rt){
if(l == r){
sum[rt] += add;
}
else{
int m = (l + r)>>1;
if(p <=m)
Update(p,add,lson);
else
Update(p,add,rson);
Pushup(rt);
}
}
int Query(int L,int R,int l,int r,int rt){ //L ,R为查询范围
if(L <= l && r <=R){
return sum[rt];
}
int m = (l + r)>>1;
int ans = 0;
if(L<=m)
ans += Query(L,R,lson);
if(R>m)
ans += Query(L,R,rson);
return ans;
}
int main(){
int t;
scanf("%d",&t);
for(int cas=1;cas<=t;cas++){
printf("Case %d:\n",cas);
int n,a,b;
scanf("%d",&n);
Build(1,n,1);
char s[10];
while(scanf("%s",s)!=EOF){
if(s[0] == 'E') break;
if(s[0] == 'A'){
scanf("%d%d",&a,&b);
Update(a,b,1,n,1);
}
else if(s[0] == 'S'){
scanf("%d%d",&a,&b);
Update(a,-b,1,n,1);
}
else if(s[0] == 'Q'){
scanf("%d%d",&a,&b);
printf("%d\n",Query(a,b,1,n,1));
}
}
}
}
-
【线段树模板2,区间修改】
#include <cstdio> #include <algorithm> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 #define LL long long const int maxn = 111111; LL add[maxn<<2]; LL sum[maxn<<2]; void PushUp(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void PushDown(int rt,int m) { if (add[rt]) { add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt]; sum[rt<<1] += add[rt] * (m - (m >> 1)); sum[rt<<1|1] += add[rt] * (m >> 1); add[rt] = 0; } } void build(int l,int r,int rt) { add[rt] = 0; if (l == r) { scanf("%lld",&sum[rt]); return ; } int m = (l + r) >> 1; build(lson); build(rson); PushUp(rt); } void update(int L,int R,int c,int l,int r,int rt) { if (L <= l && r <= R) { add[rt] += c; sum[rt] += (LL)c * (r - l + 1); return ; } PushDown(rt , r - l + 1); int m = (l + r) >> 1; if (L <= m) update(L , R , c , lson); if (m < R) update(L , R , c , rson); PushUp(rt); } LL query(int L,int R,int l,int r,int rt) { if (L <= l && r <= R) { return sum[rt]; } PushDown(rt , r - l + 1); int m = (l + r) >> 1; LL ret = 0; if (L <= m) ret += query(L , R , lson); if (m < R) ret += query(L , R , rson); return ret; } int main() { int N , Q; scanf("%d%d",&N,&Q); build(1 , N , 1); while (Q --) { char op[2]; int a , b , c; scanf("%s",op); if (op[0] == 'Q') { scanf("%d%d",&a,&b); printf("%lld\n",query(a , b , 1 , N , 1)); } else { scanf("%d%d%d",&a,&b,&c); update(a , b , c , 1 , N , 1); } } return 0; }
【KMP】
#include <iostream> #include <cstdio> using namespace std; const int maxn =1000005; const int maxb =10005; int a[maxn],b[maxb]; int nextv[maxb]; void get_next(int b[],int m){ //求模式串的 next数组 int i=0;//前缀 nextv[0]=-1; int j=-1;//后缀 while(i<m){ if(j==-1||b[i]==b[j]){ i++; j++; if(b[i]==b[j]) //遇到相同元素的优化 nextv[i]=nextv[j]; else nextv[i]=j; } else j=nextv[j]; } } void getPrenext(int b[],int m){ //未优化的getnext , 可以求模式串前后缀相同次数 int i = 0, j = -1; next[0] = -1; while(i<m){ if(j == -1 || b[i] == b[j]){ nextv[++i] = ++j; } else j = nextv[j]; } } int KMP(int n,int m){ int i=0; int j=0; while(i<n&&j<m){ if(j==-1||a[i]==b[j]){ i++;j++; } else{ j=nextv[j]; } } if(j==m){ //只有匹配出结果j才能==m // cout<<"i: "<<i<<" "<<j<<endl; return i-m+1; } else return -1; }
AC自动机
#include <iostream> #include <cstring> #include <cstdio> using namespace std; struct node{ node *next[26]; int count ; //记录 node* fail; node(){ count = 0; fail = NULL; memset(next,0,sizeof(next)); } }*q[5000000]; int head,tail; char str[1000010]; node *root; void insert(char *s){ //构建trie int len = strlen(s); node *p = root; for(int i=0;i<len;i++){ int index = s[i]-'a'; if(!p->next[index]) p->next[index] = new node; p=p->next[index]; } p->count++; } void build_ac_automation(){ //初始化fail指针 q[tail++] = root; while(head<tail){ node *p = q[head++]; node *tmp = NULL; for(int i=0;i<26;i++){ if(p->next[i] != NULL){ if(p == root)//首元素必须指根 p->next[i]->fail = root; else{ tmp = p->fail; //失败指针(跳转指针) while(tmp != NULL){ if(tmp->next[i] != NULL){//找到匹配 p->next[i]->fail = tmp->next[i]; break; } //如果没找到,则继续向上一个失败指针找 tmp = tmp->fail; } if(tmp == NULL) //为空 则从头匹配 p->next[i]->fail = root; } q[tail++] = p->next[i];//下一层入队 } } } } int query(){ int len = strlen(str); node *p = root; int cnt = 0; for(int i=0;i<len;i++){ int index = str[i]-'a'; while(p->next[index] == NULL && p!=root) p = p->fail; p = p->next[index]; if(p == NULL) p = root; node *tmp = p;//tmp 动 , p不动。 while(tmp != root && tmp->count != -1){ cnt += tmp->count; tmp->count = -1; tmp = tmp ->fail; } } return cnt; } int main(){ int t; scanf("%d",&t); while(t--){ head= tail = 0; root = new node; int n; scanf("%d",&n); char s[10010]; for(int i=0;i<n;i++){ scanf("%s",s); insert(s); } build_ac_automation(); scanf("%s",str); int ans = query(); printf("%d\n",ans); } return 0; }
最小表示法
int minRepresentation(char *s){ int i=0,j=1,k=0; while(i<len && j<len && k<len){ int tmp = s[(i + k)%len] - s[(j + k)%len]; if(tmp == 0) k++; //相等 else{ if(tmp > 0) //改成 tmp > 0 就是最大表示法了 i += k+1; // i = i + k + 1; else j += k+1; if(i == j) j++; k = 0; } } return min(i,j); //返回字典序最小的下标 }