(考前复习)
一、图论
1、最小生成树
(1)Kruskal

1 #include <algorithm> 2 #include <cstdio> 3 #define ll long long 4 struct node{ 5 int u,v,w; 6 }g[200005]; 7 int cnt,fa[100005],num,n,m; 8 void add(int x,int y,int z) 9 { 10 g[++num].u=x; g[num].v=y; g[num].w=z; 11 return; 12 } 13 bool cmp(node x,node y) 14 { 15 return x.w<y.w; 16 } 17 int find(int x) 18 { 19 if (x==fa[x]) return x; 20 return fa[x]=find(fa[x]); 21 } 22 ll Kruskal() 23 { 24 if (n==1) return 0; 25 int i,cnt=0,fu,fv; 26 ll tot=0;// 最小生成树权值和 27 std::sort(g+1,g+m+1,cmp); 28 for (i=1;i<=n;i++) fa[i]=i; 29 for (i=1;i<=m;i++) 30 { 31 fu=find(g[i].u); 32 fv=find(g[i].v); 33 if (fu==fv) continue; 34 fa[fv]=fu; 35 tot+=g[i].w; 36 cnt++; 37 if (cnt==n-1) return tot; 38 } 39 } 40 int main() 41 { 42 int i,j,x,y,z; 43 scanf("%d%d",&n,&m); // n个 顶点,m 条边 44 for (i=1;i<=m;i++) 45 scanf("%d%d%d",&x,&y,&z), 46 add(x,y,z); // x 和 y 有一条权值为 z 的边 47 printf("%lld",Kruskal()); 48 return 0; 49 }
(2)Prim

1 #include <algorithm> 2 #include <cstring> 3 #include <cstdio> 4 #define ll long long 5 struct node{ 6 int u,w,nex; 7 }g[200005]; 8 int num,n,m,fir[100005]; 9 bool vis[100005]; 10 ll dis[100005]; 11 void add(int x,int y,int z) 12 { 13 g[++num].u=y; g[num].w=z; g[num].nex=fir[x]; fir[x]=num; 14 return; 15 } 16 ll Prim() 17 { 18 int i,j,k,p,cnt=0; 19 ll tot=0,minv; // 最小生成树权值和 20 memset(dis,127,sizeof(dis)); 21 dis[1]=0; 22 while (cnt<n) 23 { 24 for (p=-1,minv=3e9,j=1;j<=n;j++) 25 if (!vis[j] && dis[j]<minv) 26 minv=dis[j],p=j; 27 tot+=dis[p]; vis[p]=1; cnt++; 28 for (k=fir[p];k;k=g[k].nex) 29 if (!vis[g[k].u] && dis[g[k].u]>g[k].w) 30 dis[g[k].u]=g[k].w; 31 } 32 return tot; 33 } 34 int main() 35 { 36 int i,j,x,y,z; 37 scanf("%d%d",&n,&m); // n 个顶点,m 条边 38 for (i=1;i<=m;i++) 39 scanf("%d%d%d",&x,&y,&z), 40 add(x,y,z),add(y,x,z); // x 和 y 之间有一条权值为 z 的边 41 printf("%lld",Prim()); 42 return 0; 43 }
2、最短路
(1)SPFA

1 #include <algorithm> 2 #include <cstring> 3 #include <cstdio> 4 #define ll long long 5 const int maxn=10005; 6 struct node{ 7 int u,w,nex; 8 }g[500005]; 9 int num,n,m,fir[10005],s,q[10005],h,t; 10 bool vis[10005]; 11 ll dis[10005]; 12 void add(int x,int y,int z) 13 { 14 g[++num].u=y; g[num].w=z; g[num].nex=fir[x]; fir[x]=num; 15 return; 16 } 17 void spfa() 18 { 19 int i,j,k,u,v; 20 for (i=1;i<=n;i++) dis[i]=2147483647; 21 dis[s]=0; h=0; t=1; q[1]=s; vis[s]=1; 22 while (h!=t) 23 { 24 (++h)%=maxn; u=q[h]; vis[u]=0; 25 for (k=fir[u];k;k=g[k].nex) 26 { 27 v=g[k].u; 28 if (dis[u]+g[k].w<dis[v]) 29 { 30 dis[v]=dis[u]+g[k].w; 31 if (!vis[v]) 32 { 33 vis[v]=1; 34 (++t)%=maxn; 35 q[t]=v; 36 } 37 } 38 } 39 } 40 return; 41 } 42 int main() 43 { 44 int i,j,x,y,z; 45 scanf("%d%d%d",&n,&m,&s); // n 个顶点,m 条边,起点 s 46 for (i=1;i<=m;i++) 47 scanf("%d%d%d",&x,&y,&z), 48 add(x,y,z); // 一条从 x 指向 y 的边权值为 z 49 spfa(); 50 for (i=1;i<=n;i++) printf("%d ",dis[i]); 51 return 0; 52 }
(2)SPFA双端队列优化

1 #include <cstring> 2 #include <cstdio> 3 const int maxn=20005; 4 struct node{ 5 int u,w,nex; 6 }g[100005]; 7 int n,m,fir[10005],num,q[20005],dis[10005]; 8 bool vis[10005]; 9 void add(int x,int y,int z) 10 { 11 g[++num].u=y; g[num].w=z; g[num].nex=fir[x]; fir[x]=num; 12 return; 13 } 14 void spfa(int S) 15 { 16 int u,v,i,h,t; 17 memset(dis,63,sizeof(dis)); 18 memset(vis,0,sizeof(vis)); 19 dis[S]=0; 20 h=0; t=1; q[1]=S; vis[S]=1; 21 while (h!=t) 22 { 23 (++h)%=maxn; 24 u=q[h]; vis[u]=0; 25 for (i=fir[u];i;i=g[i].nex) 26 { 27 v=g[i].u; 28 if (dis[v]>dis[u]+g[i].w) 29 { 30 dis[v]=dis[u]+g[i].w; 31 if (!vis[v]) 32 { 33 vis[v]=1; 34 if (dis[v]<dis[q[(h+1)%maxn]]) 35 q[h]=v,h=(h-1+maxn)%maxn; 36 else (++t)%=maxn,q[t]=v; 37 } 38 } 39 } 40 } 41 return; 42 } 43 int main() 44 { 45 int i,j,t,p,x,y,z; 46 scanf("%d%d",&n,&m); 47 for (i=1;i<=m;i++) 48 scanf("%d%d%d",&x,&y,&z), 49 add(x,y,z),add(y,x,z); 50 spfa(1); 51 return 0; 52 }
(3)Dijkstra优先队列优化

1 #include <cstdio> 2 #include <vector> 3 #include <queue> 4 #define ll long long 5 #define pa pair<ll,int> 6 using namespace std; 7 struct node{ 8 int u,w,nex; 9 }g[1000005]; 10 int n,m,fir[500005],num; 11 ll dis[500005]; 12 priority_queue <pa,vector<pa>,greater<pa> > q; 13 void add(int x,int y,int z) 14 { 15 g[++num].u=y; g[num].w=z; g[num].nex=fir[x]; fir[x]=num; 16 return; 17 } 18 void dijkstra(int S) 19 { 20 int h=0,t=1,u,v,i; 21 for (i=1;i<=n;i++) dis[i]=1e15; 22 dis[S]=0; 23 q.push(make_pair(0,S)); 24 while (!q.empty()) 25 { 26 u=q.top().second; q.pop(); 27 for (i=fir[u];i;i=g[i].nex) 28 { 29 v=g[i].u; 30 if (dis[v]>dis[u]+g[i].w) 31 dis[v]=dis[u]+g[i].w, 32 q.push(make_pair(dis[v],v)); 33 } 34 } 35 return; 36 } 37 int main() 38 { 39 int i,j,x,y,z,p; 40 scanf("%d%d",&n,&m); 41 for (i=1;i<=m;i++) 42 scanf("%d%d%d",&x,&y,&z), 43 add(x,y,z),add(y,x,z); 44 dijkstra(1); 45 return 0; 46 }
(4)Dijkstra手打堆优化

1 #include <cstring> 2 #include <cstdio> 3 struct node{ 4 int u,w,nex; 5 }g[400005]; 6 struct note{ 7 int dis,id; 8 }h[100005]; 9 int n,m,fir[100005],d[100005],num; 10 int pos[100005],siz; 11 void add(int x,int y,int z) 12 { 13 g[++num].u=y; g[num].w=z; g[num].nex=fir[x]; fir[x]=num; 14 return; 15 } 16 void put(int x) 17 { 18 int fa; 19 note t; 20 while (x>1) 21 { 22 fa=x>>1; 23 if (h[x].dis>=h[fa].dis) break; 24 pos[h[x].id]=fa; pos[h[fa].id]=x; 25 t=h[x]; h[x]=h[fa]; h[fa]=t; 26 x=fa; 27 } 28 return; 29 } 30 void get(int x) 31 { 32 int son; 33 note t; 34 h[1]=h[siz--]; 35 while (x*2<=siz) 36 { 37 son=x<<1; 38 if (son<siz && h[son].dis>h[son+1].dis) son++; 39 if (h[x].dis<=h[son].dis) break; 40 pos[h[x].id]=son; pos[h[son].id]=x; 41 t=h[x]; h[x]=h[son]; h[son]=t; 42 x=son; 43 } 44 return ; 45 } 46 void dijkstra() 47 { 48 int i,j,u,v,p; 49 note top; 50 for (i=1;i<=n;i++) 51 h[++siz].dis=d[i],h[siz].id=i,pos[i]=siz; 52 for (i=1;i<=n;i++) 53 { 54 top=h[1]; get(1); pos[top.id]=0; 55 for (j=fir[top.id];j;j=g[j].nex) 56 { 57 v=g[j].u; 58 if (pos[v]>0 && top.dis+g[j].w<h[pos[v]].dis) 59 p=pos[v], 60 h[p].dis=top.dis+g[j].w, 61 d[v]=h[p].dis, 62 put(p); 63 } 64 } 65 return; 66 } 67 int main() 68 { 69 int i,j,k,x,y,z; 70 memset(fir,0,sizeof(fir)); 71 memset(d,63,sizeof(d)); 72 scanf("%d%d",&n,&m); 73 d[1]=num=siz=0; 74 for (i=1;i<=m;i++) 75 scanf("%d%d%d",&x,&y,&z), 76 add(x,y,z); 77 dijkstra(); 78 for (i=1;i<=n;i++) 79 printf("%d ",d[i]); 80 return 0; 81 }
(5)Floyd

1 #include<cstdio> 2 const int inf=1e9; 3 int m,n; 4 int map[505][505],b[505],path[505][505]; //path[i][j]记录路径 5 void floyd() 6 { 7 int i,j,k; 8 for(k=1;k<=n;k++) 9 for(i=1;i<=n;i++) 10 for(j=1;j<=n;j++) 11 if(map[i][j]>map[i][k]+map[k][j]) 12 map[i][j]=map[i][k]+map[k][j], 13 path[i][j]=path[i][k]; 14 return; 15 } 16 int main() 17 { 18 int i,j,u,v,len; 19 scanf("%d%d",&n,&m); //输入城市数量 和 道路数量 20 //初始化 21 for(i=1;i<=n;i++) 22 for(j=1;j<=n;j++) 23 map[i][j]=inf, 24 path[i][j]=j; 25 while(m--) 26 scanf("%d%d%d",&u,&v,&len), 27 map[u][v]=map[v][u]=len; 28 floyd();//进行每对节点的求最小路径 29 while(scanf("%d%d",&u,&v)) 30 {//输入起点和终点 31 int tmp=u; 32 printf("%d",u); 33 while(tmp!=v)//打印路径 34 printf("-->%d",path[tmp][v]), 35 tmp=path[tmp][v]; 36 //输出最小花费 37 printf(" "); 38 printf("cost: %d",map[u][v]); 39 } 40 return 0; 41 }
3、强连通分量
(1)Kosaraju

1 #include <cstdio> 2 struct note{ 3 int u,nex; 4 }; 5 note g[40005],gp[40005]; 6 int fir[205],firp[205],s,t,q[205],m,n; 7 int mark[205]; // 点u属于哪个连通块 8 bool vis[205]; 9 void ADD(int k,int x,int y) 10 { 11 g[k].u=y; g[k].nex=fir[x]; fir[x]=k; 12 gp[k].u=x; gp[k].nex=firp[y]; firp[y]=k; 13 } 14 void dfs1(int u) 15 { 16 vis[u]=true; 17 for (int k=fir[u];k;k=g[k].nex) 18 if (!vis[g[k].u]) 19 dfs1(g[k].u); 20 q[++t]=u; 21 return ; 22 } 23 void dfs2(int u,int t) 24 { 25 vis[u]=true; 26 mark[u]=t; 27 for (int k=firp[u];k;k=gp[k].nex) 28 if (!vis[gp[k].u]) 29 dfs2(gp[k].u,t); 30 return; 31 } 32 void scc() 33 { 34 int i; 35 for (i=1;i<=n;i++) 36 if (!vis[i]) 37 dfs1(i); 38 for (i=0;i<=n;i++) 39 vis[i]=false; 40 for (i=n;i>=1;i--) 41 if (!vis[q[i]]) 42 dfs2(q[i],++s); 43 return; 44 } 45 int main() 46 { 47 int i,j,k,x,y; 48 scanf("%d%d",&n,&m); 49 for (i=1;i<=m;i++) 50 scanf("%d%d",&x,&y), 51 ADD(++t,x,y); 52 t=0; 53 scc(); 54 printf("%d",s); // 输出强连通分量个数 55 return 0; 56 }
(2)tarjan

1 #include <cstdio> 2 struct note{ 3 int x,nex; 4 }; 5 note g[8005]; 6 int fir[3005],stack[3005],dfn[3005],low[3005],zone[3005],ind[3005]; 7 int m,n,a,b,s,t,p,num; 8 long long mark[3005]; 9 bool vis[3005]; // 标记结点是否在栈里 10 void ADD(int k,int x,int y) 11 { 12 g[k].x=y; g[k].nex=fir[x]; fir[x]=k; 13 } 14 void tarjan(int u) 15 { 16 stack[++t]=u; 17 vis[u]=true; 18 low[u]=dfn[u]=++s; 19 for (int k=fir[u];k;k=g[k].nex) 20 { 21 int v=g[k].x; 22 if (!dfn[v]) 23 { 24 tarjan(v); 25 if (low[v]<low[u]) 26 low[u]=low[v]; 27 } 28 else 29 if (vis[v] && dfn[v]<low[u]) 30 low[u]=dfn[v]; 31 } 32 if (low[u]==dfn[u]) // 找到一个强连通分量 33 { 34 num++; int v,tt=t; 35 do 36 { 37 v=stack[t]; 38 vis[v]=false; 39 zone[v]=num; // 标记结点所属强连通分量 40 t--; 41 }while (u!=v); 42 } 43 return; 44 } 45 int main() 46 { 47 int i,j,k; 48 long long x; 49 scanf("%d",&n); 50 scanf("%d",&m); 51 for (i=1;i<=m;i++) 52 { 53 scanf("%d%d",&a,&b); 54 ADD(++t,a,b); 55 } 56 t=0; 57 for (i=1;i<=n;i++) 58 if (!dfn[i]) 59 tarjan(i); 60 for (i=1;i<=n;i++) // 缩点 61 for (k=fir[i];k;k=g[k].nex) 62 { 63 int v=g[k].x; 64 if (zone[i]!=zone[v]) 65 ind[zone[v]]++; 66 } 67 return 0; 68 }
4、割点与桥

1 void tarjan(int u,int fa) 2 { 3 int cnt=0; 4 low[u]=dfn[u]=++idx; 5 for(int i=0;i<(int)adj[u].size();i++) 6 { 7 int v=adj[u][i]; 8 if(v==fa) continue; 9 if(!dfn[v]) 10 { 11 tarjan(v,u); 12 ++cnt; 13 low[u]=min(low[u],low[v]); 14 if( (root==u&&cnt>1)||(root!=u&&dfn[u]<=low[v]) ) //判断是否是割点 15 isap[u]=1; 16 if(dfn[u]<low[v]) cutE[++numE]=Edge(u,v);//判断是否是桥,视具体情况采用恰当的结构记录。 17 } 18 else low[u]=min(low[u],dfn[v]);//这里不用判断是否点v在栈中 19 } 20 }
5、点双连通分量

1 /*无向图的双连通分量: 2 3 割顶的bccno无意义:割点的bccno会被多次赋值,所以//它的值无意义。 4 5 调用结束后, S保证为空: 6 对于点双连通分支,实际上在求割点的过程中就能顺便把每个点双连通分支求出。 7 建立一个栈,存储当前双连通分支,在搜索图时,每找到一条树枝边或后向边(非横叉边), 8 就把这条边加入栈中。如果遇到某时满足DFS(u)<=Low(v),说明u是一个割点, 9 同时把边从栈顶一个个取出,直到遇到了边(u,v),取出的这些边与其关联的点, 10 组成一个点双连通分支。割点可以属于多个点双连通分支,其余点和每条边只属于且 11 属于一个点双连通分支。*/ 12 13 #include <iostream> 14 #include <cstdlib> 15 #include <cstdio> 16 #include <algorithm> 17 #include <cstring> 18 #include <stack> 19 #include <vector> 20 #include <queue> 21 #include <map> 22 23 using namespace std; 24 25 const int maxn = 6; 26 27 int pre[maxn], iscut[maxn], bccno[maxn], low[maxn],dfs_clock, bcc_cnt; 28 vector<int> G[maxn], bcc[maxn]; 29 30 struct Edge{ 31 int u, v; 32 }; 33 stack<Edge> S; 34 int dfs(int u, int fa){ 35 int lowu = pre[u] = ++dfs_clock; 36 int child = 0; 37 // printf("u%d ",u); 38 for(int i=0; i<G[u].size(); i++){ 39 int v = G[u][i]; 40 Edge e = (Edge){u, v}; 41 if(!pre[v]){ 42 S.push(e); 43 child++; 44 int lowv = dfs(v, u); 45 lowu = min(lowu, lowv); 46 if(lowv >= pre[u]){ 47 iscut[u] = true; 48 bcc_cnt++; bcc[bcc_cnt].clear(); 49 for(;;) 50 { 51 Edge x = S.top(); S.pop(); 52 if(bccno[x.u] != bcc_cnt) 53 { 54 bcc[bcc_cnt].push_back(x.u); 55 bccno[x.u] = bcc_cnt; 56 } 57 if(bccno[x.v] != bcc_cnt) { 58 bcc[bcc_cnt].push_back(x.v); 59 bccno[x.v] = bcc_cnt; 60 } 61 if(x.u == u && x.v == v) break; 62 } 63 } 64 } 65 else if(pre[v] < pre[u] && v != fa){ 66 S.push(e); 67 lowu = min(lowu, pre[v]); 68 // printf("fan lowu%d ",lowu); 69 // printf("fan%d %d ",e.u,e.v); 70 } 71 } 72 if(fa < 0 && child == 1) iscut[u] = 0; 73 low[u]=lowu; 74 return lowu; 75 } 76 77 void find_bcc(int n){ 78 memset(pre, 0, sizeof(pre)); 79 memset(iscut, 0, sizeof(iscut)); 80 memset(bccno, 0, sizeof(bccno)); 81 dfs_clock = bcc_cnt = 0; 82 for(int i=0; i<n; i++){ 83 if(!pre[i]) dfs(i, -1); 84 } 85 } 86 87 int main(){ 88 int m, u, v, n; 89 freopen("test2.in","r",stdin); 90 scanf("%d%d", &n, &m); 91 92 for(int i=0; i<m; i++){ 93 cin>>u>>v; 94 G[u].push_back(v); 95 G[v].push_back(u); 96 } 97 98 find_bcc(n); 99 100 printf("%d ", bcc_cnt); //双连通分量的个数 101 for(int i=1; i<=bcc_cnt; i++){ //输出每个双连通分量 102 for(int j=0; j<bcc[i].size(); j++){ 103 printf("%d ", bcc[i][j]); 104 } 105 putchar(' '); 106 } 107 108 return 0; 109 }
6、边双连通分量

1 //边双连通分量的求解非常简单,因为边双连通分量之间没有公共边,而且桥不在任意一个边双连通分量中,所以算法十分简单,即先一次DFS找到所有桥,再一次DFS(排除了桥)找到边双连通分量。 2 //PS:当然可以用一次DFS实现。 3 struct Edge{ 4 int u,v; 5 Edge(int u=0,int v=0):u(u),v(v){} 6 }e[maxm]; 7 int n,m,stamp,dfn[maxn],low[maxn],bccno[maxn],bcc_cnt; 8 vector<int> vec[maxn],bcc[maxn]; 9 bool g[maxn][maxn],isbridge[maxm]; 10 11 void tarjan(int u,int fa) 12 { 13 int tmp; 14 dfn[u]=low[u]=++stamp; 15 for(int i=0;i<vec[u].size();i++) 16 { 17 tmp=e[vec[u][i]].v; 18 if(!dfn[tmp]) 19 { 20 tarjan(tmp,u); 21 low[u]=min(low[u],low[tmp]); 22 if(low[tmp]>dfn[u]) 23 isbridge[vec[u][i]]=isbridge[vec[u][i]^1]=1; 24 } 25 else if(dfn[tmp]<dfn[u] && tmp!=fa) 26 { 27 low[u]=min(low[u], dfn[tmp]); 28 } 29 } 30 } 31 32 void dfs(int u) 33 { 34 dfn[u]=1; 35 bccno[u]=bcc_cnt; 36 for(int i=0;i<vec[u].size();i++) 37 { 38 int tmp=vec[u][i]; 39 if(isbridge[tmp]) 40 continue; 41 if(!dfn[e[tmp].v]) 42 { 43 dfs(e[tmp].v); 44 } 45 } 46 } 47 48 void find_ebcc(){ 49 bcc_cnt=stamp=0; 50 memset(dfn,0,sizeof(dfn)); 51 memset(low,0,sizeof(low)); 52 memset(isbridge,0,sizeof(isbridge)); 53 memset(bccno,0,sizeof(bccno)); 54 memset(bcc,0,sizeof(bcc)); 55 for(int i=1;i<=n;i++) 56 if(!dfn[i]) 57 tarjan(i, -1); 58 memset(dfn,0,sizeof(dfn)); 59 for(int i=1;i<=n;i++) 60 { 61 if(!dfn[i]) 62 { 63 bcc_cnt++; 64 dfs(i); 65 } 66 } 67 }
二、数论
1、最大公因数

1 int gcd(int a,int b) 2 { 3 return b?gcd(b,a%b):a; 4 }
2、最小公倍数

1 int gcd(int a,int b) 2 { 3 return b?gcd(b,a%b):a; 4 } 5 int lcm(int a,int b) 6 { 7 return a*b/gcd(a,b); 8 }
3、快速幂(ab)

1 #include <cstdio> 2 #define ll long long 3 const int maxn=1e9+7; 4 ll ksm(int a,int b) 5 { 6 ll s=1; 7 while (b) 8 { 9 if (b&1) s=s*a%maxn; 10 a=1ll*a*a%maxn; 11 b>>=1; 12 } 13 return s; 14 }
4、欧拉筛法求素数

1 #include <cstdio> 2 int p[100005],pt; // 存素数 3 bool f[100005]; 4 void makeprime() 5 { 6 int i,j; 7 for (i=2;i<=100005;i++) 8 { 9 if (!f[i]) p[++pt]=i; 10 for (j=1;j<=pt && 1ll*i*p[j]<=100005;j++) 11 { 12 f[i*p[j]]=1; 13 if (!(i%p[j])) break; 14 } 15 } 16 // for (i=1;i<=pt;i++) printf("%d ",p[i]); 17 return; 18 } 19 int main() 20 { 21 makeprime(); 22 return 0; 23 }
5、逆元求组合数

1 #include <cstdio> 2 #define ll long long 3 const ll maxn=1e9+7; 4 int n,m,K; 5 ll ans,fac[900005],inv[900005]; 6 ll C(int n,int m) // n>=m 7 { 8 return fac[n]*inv[m]%maxn*inv[n-m]%maxn; 9 } 10 ll ksm(int a,int b) 11 { 12 ll s=1; 13 while (b) 14 { 15 if (b&1) s=s*a%maxn; 16 a=1ll*a*a%maxn; 17 b>>=1; 18 } 19 return s; 20 } 21 int main() 22 { 23 int i,j; 24 scanf("%d%d",&n,&m); 25 for (fac[0]=inv[0]=i=1;i<900005;i++) fac[i]=fac[i-1]*i%maxn; 26 inv[900004]=ksm(fac[900004],maxn-2); 27 for (i=900004;i>=1;i--) inv[i]=inv[i+1]*(i+1)%maxn; 28 printf("%lld ",C(n,m)); 29 return 0; 30 }
6、杨辉三角求组合数

1 #include <cstdio> 2 const int maxn=1e9+7; 3 int c[1005][1005]; 4 int main() 5 { 6 int i,j; 7 c[0][0]=1; 8 for (i=1;i<=1000;i++) c[i][0]=c[i][i]=1; 9 for (i=2;i<=1000;i++) 10 for (j=1;j<i;j++) 11 c[i][j]=(c[i-1][j]+c[i-1][j-1])%maxn; 12 /* for (i=0;i<=10;i++) 13 { 14 for (j=0;j<=i;j++) printf("%d ",c[i][j]); 15 printf(" "); 16 } */ 17 return 0; 18 }
7、埃氏筛法求欧拉函数

1 #include <cstdio> 2 #define ll long long 3 int eul[10000005],m; 4 int main() 5 { 6 int i,j; 7 scanf("%d",&m); // 1~m 的欧拉函数 8 for (i=1;i<=m;i++) eul[i]=i; 9 for (i=2;i<=m;i++) 10 if (eul[i]==i) 11 for (j=i;j<=m;j+=i) 12 eul[j]=eul[j]/i*(i-1); 13 return 0; 14 }
8、质因数分解求欧拉函数

1 #include <cstdio> 2 #define ll long long 3 int eul[10000005],n; 4 ll euler_phi(int n) 5 { 6 ll s=n; 7 for (int i=2;i*i<=n;i++) 8 if (!(n%i)) 9 for (s=s/i*(i-1);!(n%i);n/=i); 10 if (n!=1) s=s/n*(n-1); 11 return s; 12 } 13 int main() 14 { 15 int i,j; 16 scanf("%d",&n); 17 printf("%lld",euler_phi(n)); 18 return 0; 19 }
9、扩展欧几里得(ax+by=c的一组特解x,y)

1 int exgcd(int a,int b,int &x,int &y) 2 { 3 if (!b) 4 { 5 x=1,y=0; 6 return a; 7 } 8 int ans=exgcd(b,a%b,x,y); 9 t=x,x=y,y=t-(a/b)*y; 10 return ans; 11 }
三、数据结构
1、单调队列

1 #include <cstdio> 2 int n,m,a[1000005]; 3 int q1[1000005],h1,t1,s1[1000005],p1,d1[1000005]; // 求定长滑动区间内的最大值 4 int q2[1000005],h2,t2,s2[1000005],p2,d2[1000005]; // 求定长滑动区间内的最小值 5 int main() 6 { 7 int i,j,x; 8 scanf("%d%d",&n,&m); 9 scanf("%d",&x); 10 q1[1]=q2[1]=x; 11 h1=h2=t1=t2=d1[1]=d2[1]=1; 12 for (i=2;i<=m;i++) 13 { 14 scanf("%d",&x); 15 while (q1[t1]<=x && t1>=h1) t1--; 16 q1[++t1]=x; d1[t1]=i; 17 18 while (q2[t2]>=x && t2>=h2) t2--; 19 q2[++t2]=x; d2[t2]=i; 20 } 21 s1[1]=q1[1]; s2[1]=q2[1]; 22 p1=p2=1; 23 for (i=m+1;i<=n;i++) 24 { 25 scanf("%d",&x); 26 while (q1[t1]<=x && t1>=h1) t1--; 27 q1[++t1]=x; d1[t1]=i; 28 while (i-d1[h1]>=m) h1++; 29 s1[++p1]=q1[h1]; 30 31 while (q2[t2]>=x && t2>=h2) t2--; 32 q2[++t2]=x; d2[t2]=i; 33 while (i-d2[h2]>=m) h2++; 34 s2[++p2]=q2[h2]; 35 } 36 for (i=1;i<=p2;i++) 37 printf("%d ",s2[i]); 38 printf(" "); 39 40 for (i=1;i<=p1;i++) 41 printf("%d ",s1[i]); 42 return 0; 43 }
2、字典树

1 #include <cstdio> 2 #define ll long long 3 int n,cnt=1,f[12000015][2],num[12000015]; // 树的深度 * 每个结点分叉数 * 字符个数 4 ll a[100005],ans; 5 void add(ll x) 6 { 7 int now=1,i,j; 8 for (i=0;i<60;i++) 9 { 10 j=((x&(1ll<<i))>>i); 11 if (!f[now][j]) f[now][j]=++cnt; 12 num[f[now][j]]++; 13 now=f[now][j]; 14 } 15 return; 16 } 17 void query(ll x) 18 { 19 int now=1,k,j; 20 for (now=1,j=0;j<60;j++) 21 { 22 k=((x&(1ll<<j))>>j); 23 ans+=num[f[now][k]]; 24 now=f[now][k]; 25 } 26 return; 27 } 28 int main() 29 { 30 int i,j,k,now; 31 scanf("%d",&n); 32 for (i=1;i<=n;i++) 33 scanf("%lld",&a[i]); 34 for (i=1;i<=n;i++) add(a[i]); 35 for (i=1;i<=n;i++) query(a[i]); 36 printf("%lld ",ans); 37 return 0; 38 }
3、树状数组

1 #include <cstdio> 2 int n,m,k,a[100005],c[100005]; 3 int lowbit(int x) 4 { 5 return x&(-x); 6 } 7 void plus(int p,int x) 8 { 9 while (p<=n) 10 c[p]+=x, 11 p+=lowbit(p); 12 return; 13 } 14 int getsum(int p) 15 { 16 int s=0; 17 while (p) 18 s+=c[p], 19 p-=lowbit(p); 20 return s; 21 } 22 int main() 23 { 24 int i,j,x,y; 25 scanf("%d%d",&n,&m); 26 for (i=1;i<=n;i++) 27 scanf("%d",&a[i]), 28 plus(i,a[i]); 29 while (m--) 30 { 31 scanf("%d%d%d",&k,&x,&y); 32 // k=0 求区间[x,y]的和 33 // k=1 a[x]的值加上y 34 if (k) plus(x,y); 35 else printf("%d ",getsum(y)-getsum(x-1)); 36 } 37 return 0; 38 }
4、线段树

1 #include <cstdio> 2 int n,m,s,t,p,f[4000005],a[1000005]; // f[4*n] 3 void build(int l,int r,int id) 4 { 5 if (l==r) 6 { 7 f[id]=a[l]; 8 return; 9 } 10 int ls=id<<1,rs=ls|1,mid=(l+r)>>1; 11 build(l,mid,ls); 12 build(mid+1,r,rs); 13 f[id]=f[ls]+f[rs]; 14 return; 15 } 16 void modify(int l,int r,int id) 17 { 18 if (l==r) 19 { 20 f[id]+=t; 21 return; 22 } 23 int ls=id<<1,rs=ls|1,mid=(l+r)>>1; 24 if (s<=mid) modify(l,mid,ls); 25 else modify(mid+1,r,rs); 26 f[id]=f[ls]+f[rs]; 27 return; 28 } 29 int query(int l,int r,int id) 30 { 31 if (s<=l && r<=t) return f[id]; 32 int sum=0,ls=id<<1,rs=ls|1,mid=(l+r)>>1; 33 if (s<=mid) sum=query(l,mid,ls); 34 if (t>mid) sum+=query(mid+1,r,rs); 35 return sum; 36 } 37 int main() 38 { 39 int i,j; 40 scanf("%d%d",&n,&m); 41 build(1,n,1); 42 while (m--) 43 { 44 scanf("%d%d%d",&p,&s,&t); 45 // p=0 a[s]的值加上t 46 // p=1 求区间[s,t]的和 47 if (!p) modify(1,n,1); 48 else printf("%d ",query(1,n,1)); 49 } 50 return 0; 51 }
四、动态规划
1、01背包
(1)二维

1 #include <cstdio> 2 int n,m,w[1005],c[1005],f[1005][1005]; 3 int max(int x,int y) 4 { 5 return x>y?x:y; 6 } 7 int main() 8 { 9 int i,j,k; 10 scanf("%d%d",&n,&m); 11 for (i=1;i<=n;i++) 12 scanf("%d%d",&w[i],&c[i]); 13 for (i=1;i<=n;i++) 14 for (j=0;j<=m;j++) 15 { 16 f[i][j]=f[i-1][j]; 17 if (j>=w[i]) f[i][j]=max(f[i][j],f[i-1][j-w[i]]+c[i]); 18 } 19 printf("%d",f[n][m]); 20 return 0; 21 }
(2)一维

1 #include <cstdio> 2 int n,m,w[1005],c[1005],f[1005]; 3 int max(int x,int y) 4 { 5 return x>y?x:y; 6 } 7 int main() 8 { 9 int i,j,k; 10 scanf("%d%d",&n,&m); 11 for (i=1;i<=n;i++) 12 scanf("%d%d",&w[i],&c[i]); 13 for (i=1;i<=n;i++) 14 for (j=m;j>=w[i];j--) 15 f[j]=max(f[j],f[j-w[i]]+c[i]); 16 printf("%d",f[m]); 17 return 0; 18 }
2、完全背包

1 #include <cstdio> 2 int n,m,w[1005],c[1005],f[1005]; 3 int max(int x,int y) 4 { 5 return x>y?x:y; 6 } 7 int main() 8 { 9 int i,j,k; 10 scanf("%d%d",&n,&m); 11 for (i=1;i<=n;i++) 12 scanf("%d%d",&w[i],&c[i]); 13 for (i=1;i<=n;i++) 14 for (j=w[i];j<=m;j++) 15 f[j]=max(f[j],f[j-w[i]]+c[i]); 16 printf("%d",f[m]); 17 return 0; 18 }
3、多重背包
(1)普通二维

1 #include <cstdio> 2 int n,m,w[1005],v[1005],num[1005],f[1005][2005]; 3 int min(int x,int y) 4 { 5 return x<y?x:y; 6 } 7 int max(int x,int y) 8 { 9 return x>y?x:y; 10 } 11 int main() 12 { 13 int i,j,k,t; 14 scanf("%d%d",&n,&m); 15 for (i=1;i<=n;i++) 16 scanf("%d%d%d",&w[i],&v[i],&num[i]); 17 for(i=1;i<=n;i++) 18 for(j=w[i];j<=m;j++) 19 { 20 t=min(num[i],j/w[i]);//可以放的个数 21 for(k=0;k<=t;k++) 22 f[i][j]=max(f[i][j],f[i-1][j-k*w[i]]+k*v[i]); 23 } 24 printf("%d",f[n][m]); 25 return 0; 26 }
(2)一维

1 #include <cstdio> 2 int n,m,w[1005],v[1005],num[1005],f[2005]; 3 int min(int x,int y) 4 { 5 return x<y?x:y; 6 } 7 int max(int x,int y) 8 { 9 return x>y?x:y; 10 } 11 int main() 12 { 13 int i,j,k,t; 14 scanf("%d%d",&n,&m); 15 for (i=1;i<=n;i++) 16 scanf("%d%d%d",&w[i],&v[i],&num[i]); 17 for(i=1;i<=n;i++) 18 { 19 t=min(num[i],j/w[i]);//可以放的个数 20 for(k=0;k<=t;k++) 21 for(j=m;j>=w[i]*k;j--) 22 f[j]=max(f[j],f[j-k*w[i]]+k*v[i]); 23 } 24 printf("%d",f[m]); 25 return 0; 26 }
(3)二进制优化

1 #include <cstdio> 2 int n,m,count,w[105],v[105],num[105],f[100005]; 3 int max(int x,int y) 4 { 5 return x>y?x:y; 6 } 7 int main() 8 { 9 int i,j,p,g; 10 scanf("%d%d",&n,&m); 11 for(i=1;i<=n;i++) 12 scanf("%d%d%d",&w[i],&v[i],&num[i]); 13 for(i=1;i<=n;i++) 14 { 15 p=g=0; 16 while(num[i]>g) 17 { 18 for(j=m;j>=w[i]*g;j--) 19 f[j]=max(f[j],f[j-w[i]*g]+v[i]*g); 20 num[i]-=g; g=1<<p; p++; 21 } 22 for(j=m;j>=w[i]*num[i];j--) 23 f[j]=max(f[j],f[j-w[i]*num[i]]+v[i]*num[i]); 24 } 25 printf("%d",f[m]); 26 return 0; 27 }
4、最长不下降子序列
(1)O(n2)

1 #include <cstdio> 2 int n,a[40005],f[40005],ans; 3 int main() 4 { 5 int i,j; 6 scanf("%d",&n); 7 for (i=1;i<=n;i++) scanf("%d",&a[i]); 8 f[1]=1; 9 for (i=2;i<=n;i++) 10 { 11 for (j=1;j<i;j++) 12 if (a[j]<=a[i] && f[j]>f[i]) 13 f[i]=f[j]; 14 f[i]++; 15 } 16 for (i=1;i<=n;i++) 17 if (f[i]>ans) 18 ans=f[i]; 19 printf("%d ",ans); 20 return 0; 21 }
(2)O(n log n)

1 #include <algorithm> 2 #include <cstdio> 3 int n,a[40005],d[40005]; 4 int main() 5 { 6 int i,j,len=1; 7 scanf("%d",&n); 8 for (i=1;i<=n;i++) scanf("%d",&a[i]); 9 d[1]=a[1]; //初始化 10 for (i=2;i<=n;i++) 11 { 12 if (a[i]>=d[len]) d[++len]=a[i]; //如果可以接在len后面就接上,如果是最长上升子序列,这里变成> 13 else //否则就找一个最该替换的替换掉 14 { 15 j=std::upper_bound(d+1,d+len+1,a[i])-d; //找到第一个大于它的d的下标,如果是最长上升子序列,这里变成lower_bound 16 d[j]=a[i]; 17 } 18 } 19 printf("%d ",len); 20 return 0; 21 }
5、最长公共子序列
(1)普通

1 #include <cstring> 2 #include <cstdio> 3 char a[1005],b[1005]; 4 int lena,lenb,f[1005][1005]; 5 int max(int x,int y) 6 { 7 return x>y?x:y; 8 } 9 int main() 10 { 11 int i,j; 12 scanf("%s%s",a,b); 13 lena=strlen(a); 14 lenb=strlen(b); 15 f[0][0]=(a[0]==b[0]); 16 for(i=1;i<=lena;i++) 17 for(j=1;j<=lenb;j++) 18 { 19 if(a[i-1]==b[j-1]) 20 f[i][j]=f[i-1][j-1]+1; 21 else 22 f[i][j]=max(f[i-1][j],f[i][j-1]); 23 } 24 printf("%d ",f[lena][lenb]); 25 return 0; 26 }
(2)打印路径

1 #include<cstring> 2 #include<cstdio> 3 char a[1005],b[1005]; 4 int f[1005][1005],lena,lenb,flag[1005][1005]; 5 void write(int i,int j) 6 { 7 if(i==0||j==0) return; //递归终止条件 8 if(!flag[i][j]) 9 { 10 write(i-1,j-1); 11 printf("%c",a[i-1]); 12 } 13 else if(flag[i][j]==1) 14 { 15 write(i-1,j); 16 } 17 else if(flag[i][j]=-1) 18 { 19 write(i,j-1); 20 } 21 } 22 int main() 23 { 24 int i,j; 25 scanf("%s%s",a,b); 26 lena=strlen(a); 27 lenb=strlen(b); 28 for(i=1;i<=lena;i++) 29 for(j=1;j<=lenb;j++) 30 { 31 if(a[i-1]==b[j-1]) 32 { 33 f[i][j]=f[i-1][j-1]+1; 34 flag[i][j]=0;///来自于左上方 35 } 36 else 37 { 38 if(f[i-1][j]>f[i][j-1]) 39 { 40 f[i][j]=f[i-1][j]; 41 flag[i][j]=1;///来自于左方 42 } 43 else 44 { 45 f[i][j]=f[i][j-1]; 46 flag[i][j]=-1;///来自于上方 47 } 48 } 49 } 50 write(lena,lenb); 51 return 0; 52 }
6、区间动规

1 int dp(int l,int r) 2 { 3 if (used[l][r]) return f[l][r]; 4 used[l][r]=1; 5 if (l==r) return f[l][r]=0; 6 for (int i=l;i<r;i++) 7 f[l][r]=min(f[l][r],dp(l,i)+dp(i+1,r)+w[l][r]); 8 return f[l][r]; 9 }
五、高精度
1、高精加高精

1 #include <cstring> 2 #include <cstdio> 3 struct note{ 4 int len,v[1005]; 5 }; 6 int max(int x,int y) 7 { 8 return x>y?x:y; 9 } 10 note operator + (note a,note b) 11 { 12 note c; 13 memset(c.v,0,sizeof(c.v)); 14 int i,lenc=max(a.len,b.len); 15 for (i=1;i<=lenc;i++) 16 c.v[i]+=a.v[i]+b.v[i], 17 c.v[i+1]+=c.v[i]/10, 18 c.v[i]%=10; 19 while (c.v[lenc+1]) lenc++; 20 c.len=lenc; 21 return c; 22 }
2、高精减高精

1 #include <cstdio> 2 struct note{ 3 int len,v[10005]; 4 }; 5 char s1[10005],s2[10005]; 6 bool operator < (note a,note b) 7 { 8 if (a.len!=b.len) return a.len<b.len; 9 for (int i=a.len;i>=1;i--) 10 if (a.v[i]!=b.v[i]) 11 return a.v[i]<b.v[i]; 12 return 0; 13 } 14 note operator - (note a,note b) 15 { 16 note c; 17 int fg=1; 18 if (a<b) fg=-1,c=a,a=b,b=c; 19 int i,lenc=a.len; 20 for (i=1;i<=lenc;i++) 21 { 22 if (a.v[i]<b.v[i]) a.v[i]+=10,a.v[i+1]--; 23 c.v[i]=a.v[i]-b.v[i]; 24 } 25 while (!c.v[lenc] && lenc>1) lenc--; 26 c.len=lenc; 27 c.v[lenc]*=fg; 28 return c; 29 }
3、高精乘单精

1 #include <cstdio> 2 struct note{ 3 int v[1000],len; 4 }; 5 note operator * (note a,int b) 6 { 7 note c; 8 int w=0; 9 int i,lena=a.len,lenc; 10 for (i=1;i<=lena;i++) 11 c.v[i]=a.v[i]*b+w, 12 w=c.v[i]/10, 13 c.v[i]%=10; 14 for(lenc=lena;w;c.v[++lenc]=w%10,w/=10); 15 c.len=lenc; 16 return c; 17 }
4、高精乘高精

1 #include <cstring> 2 #include <cstdio> 3 struct note{ 4 int len,v[10005]; 5 }; 6 note operator * (note a,note b) 7 { 8 note c; 9 memset(c.v,0,sizeof(c.v)); 10 int i,j,lena=a.len,lenb=b.len,lenc=a.len+b.len-1; 11 for (i=1;i<=lena;i++) 12 for (j=1;j<=lenb;j++) 13 c.v[i+j-1]+=a.v[i]*b.v[j]; 14 for (i=1;i<=lenc;i++) 15 c.v[i+1]+=c.v[i]/10, 16 c.v[i]%=10; 17 if (c.v[lenc+1]) lenc++; 18 for (;c.v[lenc]>=10;lenc++) c.v[lenc+1]=c.v[lenc]/10,c.v[lenc]%=10; 19 c.len=lenc; 20 return c; 21 }
5、高精除单精

1 #include <cstdio> 2 struct note{ 3 int v[1000],len; 4 }; 5 note operator / (note a,int b) 6 { 7 note c; 8 int i,lena=a.len,lenc=1,x=0,t; 9 for (i=lena,x=a.v[lena];x<b && i>1;x=x*10+a.v[--i]); 10 c.v[1]=x/b; x%=b; 11 for (i--;i>=1;i--) 12 x=x*10+a.v[i], 13 c.v[++lenc]=x/b, 14 x%=b; 15 for (i=1;i<=lenc/2;i++) 16 c.v[i]^=c.v[lenc-i+1],c.v[lenc-i+1]^=c.v[i],c.v[i]^=c.v[lenc-i+1]; 17 c.len=lenc; 18 return c; 19 }
六、其它
1、读入优化

1 int read() 2 { 3 int s=0; 4 char ch=getchar(); 5 while (ch<'0' || ch>'9') ch=getchar(); 6 while (ch>='0' && ch<='9') 7 s=s*10+ch-'0',ch=getchar(); 8 return s; 9 }
2、输出优化

1 void write(int x) 2 { 3 if (x>=10) write(x/10); 4 putchar(x%10+'0'); 5 return; 6 }
3、倍增LCA

1 #include <cstdio> 2 struct node{ 3 int u,v,nex; 4 }g[1000005]; 5 int n,Q,root,fir[500005],num,f[500005][20],d[500005]; 6 void add(int x,int y) 7 { 8 g[++num].u=y; g[num].nex=fir[x]; fir[x]=num; 9 return; 10 } 11 void dfs(int x,int fa) 12 { 13 int i,k,v; 14 f[x][0]=fa; d[x]=d[fa]+1; 15 for (i=1;i<=19;i++) 16 f[x][i]=f[f[x][i-1]][i-1]; 17 for (k=fir[x];k;k=g[k].nex) 18 { 19 v=g[k].u; 20 if (v==fa) continue; 21 dfs(v,x); 22 } 23 return; 24 } 25 int lca(int x,int y) 26 { 27 int i,j; 28 if (d[x]>d[y]) x^=y,y^=x,x^=y; 29 for (i=19;i>=0;i--) 30 if (d[f[y][i]]>=d[x]) 31 y=f[y][i]; 32 if (x==y) return x; 33 for (i=19;i>=0;i--) 34 if (f[x][i]!=f[y][i]) 35 x=f[x][i],y=f[y][i]; 36 return f[x][0]; 37 } 38 int main() 39 { 40 int i,j,k,x,y; 41 scanf("%d%d%d",&n,&Q,&root); 42 for (i=1;i<n;i++) 43 scanf("%d%d",&x,&y), 44 add(x,y),add(y,x); 45 dfs(root,0); 46 while (Q--) 47 { 48 scanf("%d%d",&x,&y); 49 printf("%d ",lca(x,y)); 50 } 51 return 0; 52 }
4、计时函数

1 #include <cstdio> 2 #include <ctime> 3 int main() 4 { 5 clock_t st, ed; 6 st=clock(); 7 for(int i=0;i<100000000;i++) 8 { 9 i-=1; i+=1; 10 } 11 ed=clock(); 12 printf("Total time:%.2fs",(double)(ed-st)/CLOCKS_PER_SEC); 13 return 0; 14 }
暂存

1 #include <cstdio> 2 #include <set> 3 #define ll long long 4 std::set<int> s; 5 std::set<int>::iterator p; 6 int n,op,x,k,cnt[100005]; 7 ll ans; 8 int main() 9 { 10 scanf("%d",&n); 11 while (n--) 12 { 13 scanf("%d%d",&op,&x); 14 if (op==1) 15 { 16 ans+=x; 17 if (s.empty()) continue; 18 p=s.lower_bound(x); 19 if (p==s.begin()) continue; 20 k=*(--p); 21 ans-=k; 22 cnt[k]--; 23 if (!cnt[k]) s.erase(k); 24 } 25 else s.insert(x),cnt[x]++; 26 } 27 printf("%lld ",ans); 28 return 0; 29 }