板子们
1.数学
1.1、(扩展)欧几里得
void gcd(int a,int b,int &d,int &x,int &y) { if(!b) { d=a; x=1; y=0; } else { gcd(b,a%b,d,y,x); y-=x*(a/b); } }
1.2、同余方程
//解同余方程 ax≡c(mod b) //转化为ax-by=c void solve() { gcd(a,b,g,x0,y0); if(c%g) printf("no solution"); x=c/g*x0; y=c/g*y0; printf("任意一组解:%d",x); a/=g; b/=g; x=(x%b+b)%b printf("最小x正整数解:%d",x); }
扩展欧几里得、同余方程 解析:http://www.cnblogs.com/TheRoadToTheGold/p/6645383.html
1.3、快速幂
int quickmul(int a,int b,int p) { int ans=1; for(;b;b>>=1,a=a*a%p) if(b&1) ans=ans*a%p; return ans; }
1.4、素数判定
1.4.1. 费马小定理 若 a、p互质 ,则 a^(p-1) %p=1
缺陷:Carmichael数可以通过素数判定
O(次数*log)
#include<ctime> #include<cstdio> #include<cstdlib> using namespace std; typedef long long LL; LL p; bool flag; LL mul(LL a,LL b) { a%=p; b%=p; LL r=0; while(b) { if(b&1) { b--; r=(r+a)%p; } a<<=1; a%=p; b>>=1; } return r; } LL qm(LL a,LL b) { LL r=1; while(b) { if(b&1) r=mul(r,a); b>>=1; a=mul(a,a); } return r; } int main() { scanf("%lld",&p); srand(time(0)); for(int i=1;i<=15;i++) { LL a=rand()%(p-1)+1; LL x=qm(a,p-1); if(x!=1) { flag=1; break; } } if(flag) printf("No"); else printf("Yes"); }
1.4.2 Miller_Rabin
不会误判Carmichael数,误判概率为1/4^T
判断四五次大概够了
#include<ctime> #include<cstdio> #include<cstdlib> using namespace std; typedef long long LL; LL p; LL mul(LL a,LL b) { LL r=0; while(b) { if(b&1) r+=a,r%=p; a<<=1; a%=p; b>>=1; } return r; } LL qm(LL a,LL b) { LL r=1; while(b) { if(b&1) r=mul(r,a); a=mul(a,a); b>>=1; } return r; } bool Miller_Rabin() { if(p==2) return true; if(p==1 || p%2==0) return false; LL x,y,m,k=0,a; m=p-1; while(m%2==0) k++,m>>=1; int T=4; while(T--) { a=rand()%(p-1)+1; x=qm(a,m); for(int i=1;i<=k;i++) { y=mul(x,x); if(x&1 && y==1 && x!=1 && x!=p-1) return false; x=y; } if(x!=1) return false; } return true; } int main() { srand(time(0)); scanf("%lld",&p); if(Miller_Rabin()) printf("Yes"); else printf("No"); }
1.5 素数相关
1.5.1 素数线性筛
void solve() { for(int i=2;i<=N;i++) { if(!v[i]) prime[++cnt]=i; for(int j=1;j<=cnt;j++) { if(prime[j]*i>=N) break; v[prime[j]*i]=true; if(i%prime[j]==0) break; } } }
1.5.2 唯一分解定理
#include<cmath> #include<cstdio> #include<cstring> using namespace std; int p[100001],c[100001]; int main() { int n,sum; while(scanf("%d",&n)!=EOF) { memset(c,0,sizeof(c)); sum=0; int m=sqrt(n); for(int i=2;i<=m;i++) if(n%i==0) { p[++sum]=i; while(n%i==0) n/=i,c[sum]++; } if(n>1) p[++sum]=n,c[sum]++; for(int i=1;i<=sum;i++) printf("%d %d\n",p[i],c[i]); } }
1.5.3 阶乘的质因数分解
for(int i=1;prime[i]<=n;i++) { int t=n; while(t) { cnt[i]+=t/prime[i]; t/=prime[i]; } }
1.6 欧拉函数
原理 请转至http://www.cnblogs.com/TheRoadToTheGold/p/6598367.html
1.6.1 欧拉函数计算
void euler(int n) { int ans=n; for(int i=2;i*i<=n;i++) if(n%i==0) { ans=ans/i*(i-1); while(n%i==0) n/=i; } if(n>1) ans=ans/n*(n-1); }
1.6.2 欧拉筛
O(n)
void euler_table() { phi[1]=1; for(int i=2;i<=n;i++) { if(!v[i]) { prime[++cnt]=i; phi[i]=i-1; } for(int j=1;j<=cnt;j++) { if(prime[j]*i>=N) break; v[prime[j]*i]=true; if(i%prime[j]) phi[prime[j]*i]=phi[i]*(prime[j]-1); else { phi[prime[j]*i]=prime[j]*phi[i]; break; } } } }
1.6.3 埃拉托斯特尼筛
O(nlog²n)
int eratosthenes(int n) { int ans=n,k=n; int m=sqrt(n); for(int i=2;i<=m;i++) if(n%i==0) { ans=ans/i*(i-1); for(int j=1;j*i<=k;j++) v[i*j]=false; while(n%i==0) n/=i; } if(n>1) { ans=ans/n*i; for(int j=1;j*i<=k;j++) v[i*j]=false; } return ans; }
1.6.4 杜教筛
待填坑
1.7 莫比乌斯函数
原理转至 http://www.cnblogs.com/TheRoadToTheGold/p/6598088.html
1.7.1 线性筛
void mobius() { mul[1]=1; for(int i=2;i<=n;i++) { if(v[i]) { prime[++cnt]=i; mul[i]=-1; } for(int j=1;j<=cnt;j++) { if(i*prime[j]>=N) break; v[i*prime[j]]=true; if(i%prime[j]==0) { mul[i*prime[j]]=0; break; } mul[i*prime[j]]=-mul[i]; } } }
1.7.2 杜教筛
待填坑
1.8 逆元
只有 a p 互质,a才有关于p的逆元
原理请见:http://www.cnblogs.com/linyujun/p/5194184.html
1.8.1 费马小定理 限制条件:p是质数
int quickpow(int a,int b,int p) { int r=1; while(b) { if(b&1) r8=a,r%=p; b>>=1; a*=a; a%=p; } return r; } void solve(int a,int p) { if(prime(p)) printf("%d",quickpow(a,p-2,p)); }
1.8.2 扩展欧几里得
void exgcd(int a,int b,int& g,int& x,int& y) { if(!b) { g=a; x=1; y=0; } else { exgcd(b,a%b,g,y,x); y-=x*(a/b); } } void solve() { exgcd(a,b,g,x,y); if(g!=1) return; printf("%d",(x%p+p)%p); }
1.8.3 递推 限制条件:p是质数
void solve(int p) { inv[1]=1; for(int i=2;i<N;i++) inv[i]=(p-p/i)*inv[p%i]%p; }
1.9 中国剩余定理
1.9.1 大数翻倍法
#include<cstdio> using namespace std; int n,a[11],b[11]; long long get_gcd(long long a,long long b) { return !b ? a:get_gcd(b,a%b); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]); long long ans=b[1]; long long lcm=a[1]; for(int i=2;i<=n;i++) { while(ans%a[i]!=b[i]) ans+=lcm; long long gcd=get_gcd(lcm,(long long)a[i]); lcm=lcm/gcd*(long long)a[i]; } printf("%lld",ans); }
1.9.2 中国剩余定理 互质版
#include<cstdio> using namespace std; int n; int m[11],a[11]; long long M=1,ans,Mi[11],e[11]; void exgcd(long long a,long long b,long long &x,long long &y) { if(!b) x=1,y=0; else { exgcd(b,a%b,y,x); y-=x*(a/b); } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d",&m[i],&a[i]),M*=m[i]; for(int i=1;i<=n;i++) Mi[i]=M/m[i]; long long x,y; for(int i=1;i<=n;i++) { exgcd(Mi[i],m[i],x,y); x=(x%m[i]+m[i])%m[i]; e[i]=Mi[i]*x%M; } for(int i=1;i<=n;i++) ans=(ans+e[i]*a[i])%M; printf("%lld",ans); }
这两个原理见:http://www.cnblogs.com/TheRoadToTheGold/p/6638430.html
1.9.3 中国剩余定理 非互质版
待填坑
1.10 卢卡斯定理
1.10.1 互质版
1.10.1.1 预处理阶乘
#include<cstdio> using namespace std; typedef long long LL; LL f[100001]; void pre(int p) { f[0]=1; for(int i=1;i<=p;i++) f[i]=f[i-1]*i%p; } int pow(LL a,int b,int p) { LL r=1; while(b) { if(b&1) r*=a,r%=p; b>>=1; a*=a; a%=p; } return r; } int C(int n,int m,int p) { if(m>n) return 0; //如果m>n,那么原来的m<n-p,即m与n之间至少有一个p的倍数,那么结果为0 return f[n]*pow(f[m]*f[n-m],p-2,p)%p; } /*int Lucas(int n,int m,int p) { if(!m) return 1; return (C(n%p,m%p,p)*Lucas(n/p,m/p,p))%p; }*/ int Lucas(int n,int m,int p) //非递归版,上面是递归版 { LL ans=1; for(;m;n/=p,m/=p) ans=ans*C(n%p,m%p,p)%p; return ans; } int main() { int T,n,m,p; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&p); pre(p); printf("%d\n",Lucas(n,m,p)); } }
1.10.1.2 阶乘不能预处理
#include<cstdio> using namespace std; typedef long long LL; /*int pow(LL a,int b,int p) { LL r=1; while(b) { if(b&1) r*=a,r%=p; b>>=1; a*=a; a%=p; } return r; }*/ void exgcd(int a,int b,LL &x,LL &y) { if(!b) { x=1; y=0; } else { exgcd(b,a%b,y,x); y-=x*(a/b); } } int inv(int x,int p) { //return pow(x,p-2,p); LL x0,y0; exgcd(x,p,x0,y0); return (x0%p+p)%p; } int C(int n,int m,int p) { if(n<m) return 0; LL r=1; //for(int i=m+1;i<=n;i++) r=r*i%p*inv(i-m,p)%p; TLE for(int i=1;i<=m;i++) r=r*(n-i+1)%p*inv(i,p)%p; return r; } int Lucas(int n,int m,int p) { LL ans=1; for(;m;n/=p,m/=p) ans=ans*C(n%p,m%p,p)%p; return ans; } int main() { int n,m,p; while(scanf("%d%d%d",&n,&m,&p)!=EOF) printf("%d\n",Lucas(n,m,p)); }
1.10.2 扩展卢卡斯
待填坑
1.11 Catalan数
#include<cstdio> #define mod 100000007 using namespace std; long long f[20000]; int main() { int n; scanf("%d",&n); f[0]=f[1]=1; for(int i=2;i<=n;i++) for(int j=1;j<=i;j++) f[i]=(f[i]+f[j-1]*f[i-j]%mod)%mod; //for(int i=2;i<=n;i++) f[i]=f[i-1]*(4*i-2)/(i+1); printf("%lld",f[n]); }
1.12 矩阵树定理
#include<cstdio> #include<algorithm> using namespace std; int n,m; long long t,C[101][101],ans; long long Matrix_tree() { ans=1; for(int i=1;i<n;i++) { for(int j=i+1;j<n;j++) while(C[j][i]) { t=C[i][i]/C[j][i]; for(int k=i;k<n;k++) C[i][k]-=C[j][k]*t; for(int k=i;k<n;k++) swap(C[i][k],C[j][k]); ans=-ans; } if(!C[i][i]) return 0; ans*=C[i][i]; } return ans; } int main() { scanf("%d%d",&n,&m); int u,v; while(m--) { scanf("%d%d",&u,&v); u--; v--; C[u][v]--; C[v][u]--; C[u][u]++; C[v][v]++; } printf("%lld",Matrix_tree()); }
2.图论
2.1 最短路
2.1.1 Floyd
scanf("%d%d",&n,&m); memset(f,63,sizeof(f)); while(m--) { scanf("%d%d%d",&a,&b,&c); f[a][b]=f[b][a]=min(f[a][b],c); } for(int i=1;i<=n;i++) f[i][i]=0; for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
2.1.2 Dijkstra
2.1.2.1 普通Dijkstra
void dijkstra() { memset(v,false,sizeof(v)); memset(dis,63,sizeof(dis)); v[1]=true; dis[1]=0; int minn,k; for(int i=1;i<n;i++) { k=0; minn=0x7fffffff; for(int j=1;j<=n;j++) if(!v[j] && dis[j]<minn ) minn=dis[j],k=j; if(!k) break; v[k]=true; for(int j=1;j<=n;j++) if(dis[j]>dis[k]+e[k][j] ) dis[j]=dis[k]+e[k][j]; } }
2.1.2.2 堆优化
#include<cstdio> #include<queue> #include<cstring> #define N 55000 #define M 1000000 using namespace std; int n,m,s,t,tot; int nxt[M],to[M],front[N],cap[M]; int DIS[N]; bool vis[N]; struct edge { int number,dis; bool operator <(edge b) const { return dis>b.dis; } }now,nt; priority_queue<edge>q; void add(int u,int v,int w) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; cap[tot]=w; to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; cap[tot]=w; } bool dijkstra() { for(int i=1;i<=n;i++) DIS[i]=2e9; q.push((edge){s,0}); DIS[s]=0; while(!q.empty()) { now=q.top();q.pop(); if(vis[now.number]) continue; vis[now.number]=true; for(int i=front[now.number];i;i=nxt[i]) { if(DIS[to[i]]>DIS[now.number]+cap[i]) { DIS[to[i]]=DIS[now.number]+cap[i]; q.push((edge){to[i],DIS[to[i]]}); } } } printf("%d",DIS[t]); } int main() { int u,v,w; scanf("%d%d%d%d",&n,&m,&s,&t); while(m--) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); } dijkstra(); }
2.1.3 spfa
void spfa() { memset(dis,63,sizeof(dis)); dis[1]=0; q.push(1); vis[1]=true; int now; while(!q.empty()) { now=q.front(); q.pop(); vis[now]=false; for(int i=front[now];i;i=nxt[i]) if(dis[to[i]]>dis[now]+val[i]) { dis[to[i]]=dis[now]+val[i]; if(!vis[to[i]]) vis[to[i]]=true,q.push(to[i]); } } }
2.2 次短路
注:代码是严格的次短路
#include<queue> #include<cstdio> #include<cstring> #define N 5001 #define M 100001 using namespace std; int front[N],to[M<<1],nxt[M<<1],val[M<<1],from[M<<1],tot; int dis1[N],dis2[N]; bool vis[N]; queue<int>q; void add(int u,int v,int w) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w; from[tot]=u; to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w; from[tot]=v; } void spfa(int s,int *dis) { //memset(dis,63,sizeof(dis)); memset(vis,false,sizeof(vis)); q.push(s); vis[s]=true; dis[s]=0; int now; while(!q.empty()) { now=q.front(); q.pop(); vis[now]=false; for(int i=front[now];i;i=nxt[i]) if(dis[to[i]]>dis[now]+val[i]) { dis[to[i]]=dis[now]+val[i]; if(!vis[to[i]]) { vis[to[i]]=true; q.push(to[i]); } } } } int main() { int n,m; scanf("%d%d",&n,&m); int u,v,w; for(int i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); } memset(dis1,63,sizeof(dis1)); memset(dis2,63,sizeof(dis2)); spfa(1,dis1); spfa(n,dis2); int minn=dis1[n],tmp,ans=0x7fffffff; for(int i=1;i<=tot;i++) { u=from[i]; v=to[i]; tmp=dis1[u]+val[i]+dis2[v]; if(tmp>minn && tmp<ans) ans=tmp; } printf("%d",ans); }
2.3 k短路
注:代码是不严格的k短路
http://www.cnblogs.com/TheRoadToTheGold/p/7416264.html
#include<queue> #include<cstdio> #include<cstring> #define N 1001 #define M 100001 using namespace std; int n,s,t,k; int dis1[N]; bool vis[N]; int front[N],to[M],nxt[M],val[M],tot; int front2[N],to2[M],nxt2[M],val2[M],tot2; struct node { int num,dis; bool operator < (node p) const { return dis+dis1[num]>p.dis+dis1[p.num]; } }now,nt; void add(int u,int v,int w) { to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w; to2[++tot2]=u; nxt2[tot2]=front2[v]; front2[v]=tot2; val2[tot2]=w; } void init() { int m,u,v,w; scanf("%d%d",&n,&m); while(m--) { scanf("%d%d%d",&u,&v,&w); add(u,v,w); } scanf("%d%d%d",&s,&t,&k); } void spfa() { memset(dis1,63,sizeof(dis1)); queue<int>q; dis1[t]=0; vis[t]=true; q.push(t); int now; while(!q.empty()) { now=q.front(); q.pop(); vis[now]=false; for(int i=front2[now];i;i=nxt2[i]) if(dis1[to2[i]]>dis1[now]+val2[i]) { dis1[to2[i]]=dis1[now]+val2[i]; if(!vis[to2[i]]) { q.push(to2[i]); vis[to2[i]]=true; } } } } void Astar() { if(dis1[s]>1e9) { printf("-1"); return; } if(s==t) k++; int cnt=0,last=-1; priority_queue<node>q; now.num=s; now.dis=0; q.push(now); while(!q.empty()) { now=q.top(); q.pop(); if(now.num==t) { cnt++; if(cnt==k) { printf("%d",now.dis); return; } } for(int i=front[now.num];i;i=nxt[i]) { nt.num=to[i]; nt.dis=now.dis+val[i]; q.push(nt); } } printf("-1"); } int main() { init(); spfa(); Astar(); }
2.4 最小生成树
2.4.1 prim
#include<cstdio> #include<cstring> using namespace std; int a[501][501],minn[501]; bool vis[501]; int main() { int n,m; scanf("%d%d",&n,&m); int u,v,w; memset(a,63,sizeof(a)); while(m--) { scanf("%d%d%d",&u,&v,&w); if(w<a[u][v]) a[u][v]=a[v][u]=w; } memset(minn,63,sizeof(minn)); minn[1]=0; int tmp,k,ans=0; for(int i=1;i<=n;i++) { tmp=2e9; for(int j=1;j<=n;j++) if(!vis[j] && minn[j]<tmp) { tmp=minn[j]; k=j; } ans+=minn[k]; vis[k]=true; for(int j=1;j<=n;j++) if(!vis[j] && a[k][j]<minn[j]) minn[j]=a[k][j]; } printf("%d",ans); }
2.4.2 Kruskal
#include<cstdio> #include<algorithm> using namespace std; int fa[5001]; struct node { int u,v,w; }e[200001]; bool cmp(node p,node q) { return p.w<q.w; } int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); sort(e+1,e+m+1,cmp); for(int i=1;i<=n;i++) fa[i]=i; int tot=0; int r1,r2,i=1,ans=0; while(tot<n-1) { r1=find(e[i].u); r2=find(e[i].v); if(r1!=r2) { fa[r1]=r2; ans+=e[i].w; tot++; } i++; } printf("%d",ans); }
2.5 最小树形图
#include<cstdio> #include<cstring> #define N 1005 #define M 10005 #define inf 2e9 using namespace std; struct node { int u,v,w; }e[M]; int in[N],pre[N],vis[N],col[N],id[N]; int n,m; int directed_MST() { int tot=n+1,root=0,ans=0,cirnum=0,to; while(1) { for(int i=0;i<tot;i++) in[i]=inf; for(int i=1;i<=m;i++) if(e[i].u!=e[i].v && in[e[i].v]>e[i].w) { in[e[i].v]=e[i].w; pre[e[i].v]=e[i].u; } cirnum=0; memset(vis,-1,sizeof(vis)); memset(col,-1,sizeof(col)); in[root]=0; for(int i=0;i<tot;i++) { ans+=in[i]; to=i; while(vis[to]!=i && col[to]==-1 && to!=root) { vis[to]=i; to=pre[to]; } if(to!=root && col[to]==-1) { for(int nt=pre[to];nt!=to;nt=pre[nt]) col[nt]=cirnum; col[to]=cirnum++; } } if(!cirnum) return ans; for(int i=0;i<tot;i++) if(col[i]==-1) col[i]=cirnum++; for(int i=1;i<=m;i++) { to=e[i].v; e[i].u=col[e[i].u]; e[i].v=col[e[i].v]; if(e[i].u!=e[i].v) e[i].w-=in[to]; } tot=cirnum; root=col[root]; } return ans; } int main() { int tot; int u,v,w,ans; while(scanf("%d%d",&n,&m)!=EOF) { tot=0; while(m--) { scanf("%d%d%d",&u,&v,&w); if(u!=v) { u++; v++; e[++tot].u=u; e[tot].v=v; e[tot].w=w; } } m=tot; ans=directed_MST(); printf("%d",ans); } }