两年ACM竞赛的所有算法总结

1,875 阅读17分钟

这是我经过两年ACM竞赛的所有算法总结,希望对你有帮助。

目录

最短路

Floyd

Dijkstra

SPFA

最小生成树

Kruskal

Prim

动态规划

01背包

完全背包

多重背包

最长公共子序列

单调递增子序列

单调递增子序列(二分)

字符串匹配

KMP

字典树

AC自动机

博弈

巴什博奕(Bash Game)

威佐夫博弈(Wythoff Game)

尼姆博弈(Nimm Game)

大数

浮点大数加法

大数乘法

大数开方

Hash(除留余数法+链地址法)

堆排序(最小堆)

拓扑排序

归并排序

二分匹配

并查集

最大流

欧拉函数

扩展欧几里得

费马小定理


最短路

Floyd

该算法的时间复杂度是O(n*n*n),可以解决负权边的问题
举一个例子吧,给你n个点,m条双向边,求从1点到各个点的最短路径
样例:

6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
结果:

0 1 8 4 13 17
程序代码:

#include<stdio.h>
#define inf 99999999
void floyd();
int e[110][110],m,n;
int main()
{
    int i,j,a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(i==j)
                    e[i][j]=0;//自己到自己的路程为0
                else
                    e[i][j]=inf;//刚开始初始化为最大值,默认为该路不通
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(c<e[a][b])
            {
                e[a][b]=c;
                e[b][a]=c;
            }    
        }
        floyd();
        for(i=1;i<=n;i++)
            printf("%d ",e[1][i]);    
        printf("\n");
    }
    return 0;    
}
void floyd()
{
    int i,j,k;
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(e[i][j]>e[i][k]+e[k][j])
                    e[i][j]=e[i][k]+e[k][j];
}

Dijkstra

该算法的时间复杂度是O(m+n)*logn,不可以解决负权边的问题
举一个例子吧,给你n个点,m条双向边,问从1点到各个点的最短路径
样例输入:

6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
样例输出:

0 1 8 4 13 17
程序代码:

#include<stdio.h>
#include<string.h>
void dijkstra();
int e[110][110],dis[110],book[110];
int n,m;
#define inf 99999999
int main()
{
    int i,j,a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(i==j)
                    e[i][j]=0;
                else
                    e[i][j]=inf;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(c<e[a][b])
            {
                e[a][b]=c;
                e[b][a]=c;
            }
        }    
        for(i=1;i<=n;i++)
            dis[i]=e[1][i];//把从1点出发到各个点的值先存到dis数组里,先当作最短路
        memset(book,0,sizeof(book));
        book[1]=1;
        dijkstra();
        for(i=1;i<=n;i++)
            printf("%d ",dis[i]);
        printf("\n");
    }    
    return 0;
}
void dijkstra()
{
    int i,j,k,min,u;
    for(k=1;k<=n-1;k++)
    {
        min=inf;
        for(i=1;i<=n;i++)
            if(book[i]==0&&dis[i]<min)
            {
                min=dis[i];
                u=i;
            }
        book[u]=1;
        for(j=1;j<=n;j++)
            if(dis[j]>dis[u]+e[u][j])
                dis[j]=dis[u]+e[u][j];
    }
}

SPFA

该算法的时间复杂度最坏是O(m*n),可以解决负权边的问题
举一个例子吧,给你n个点,m条单向边,问从1点到各个点的最短路径
队列优化:
样例输入:

6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
样例输出:

0 1 8 4 13 17
程序代码:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
 
const int inf=1e9;
 
int n,m;
int e[110][110];
int dis[110],book[110];
 
void SPFA();
 
int main()
{
    int i,j,u,v,w;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
            {
                if(i==j)
                    e[i][j]=0;
                else
                    e[i][j]=inf;
            }
 
 
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            if(w<e[u][v])
                e[u][v]=w;
        }
        SPFA();
        for(i=1;i<=n;i++)
            printf("%d ",dis[i]);
        printf("\n");
    }
 
    return 0;
}
void SPFA()
{
    int i,u,v;
    memset(book,0,sizeof(book));
    for(i=1;i<=n;i++)
        dis[i]=inf;
 
    dis[1]=0;
    queue<int>q;
    q.push(1);
    book[1]=1;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        book[u]=0;
        for(v=1;v<=n;v++)
        {
            if(e[u][v]!=inf&&dis[u]+e[u][v]<dis[v])
            {
                dis[v]=dis[u]+e[u][v];
                if(book[v]==0)
                {
                    q.push(v);
                    book[v]=1;
                }
            }
        }
    }
}

最小生成树

Kruskal

给你n个点m条边及各边的权值,求最小生成树
样例输入:
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2
样例输出:
19
程序代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
int kruskal();
int getf(int u);
int merge(int u,int v);
int cmp(struct data x,struct data y);
int f[110],n,m;
struct data{
    int u;
    int v;
    int w;
}e[110];
int main()
{
    int i;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(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(i=1;i<=n;i++)
            f[i]=i;
        printf("%d\n",kruskal());
    }
    return 0;
}
int kruskal()
{
    int i,sum=0,count=0;
    for(i=1;i<=m;i++)
    {
        if(merge(e[i].u,e[i].v)==1)如果连接这两点,则建立该边
        {
            count++;
            sum+=e[i].w;    
        }     
        if(count==n-1)//如果建立了n-1条边,则n个点全部连完
            return sum;
    }
}
int getf(int u)//并查集寻找共同祖先
{
    if(f[u]==u)
        return u;
    f[u]=getf(f[u]);
    return f[u];
}
int merge(int u,int v)//合并两个集合
{
    u=getf(u);
    v=getf(v);
    if(u!=v)
    {
        f[v]=u;
        return 1;    
    }
    return 0;
}
int cmp(struct data x,struct data y)
{
    return x.w<y.w;
}

Prim

给你n个点m条边及各边的权值,求最小生成树
样例输入:
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2
样例输出:
19
程序代码:

#include<stdio.h>
#include<string.h>
#define inf 0x7f7f7f7f
int prim();
int n,m;
int e[110][110],book[110],dis[110];
int main()
{
    int i,j,a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(i==j)
                    e[i][j]=0;
                else
                    e[i][j]=inf;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(c<e[a][b])
                e[a][b]=e[b][a]=c;
        }    
        printf("%d\n",prim());
    }
    return 0;
}
int prim()
{
    int i,k,min,u,sum=0;
    memset(book,0,sizeof(book));
    for(i=1;i<=n;i++)
        dis[i]=e[1][i];
    book[1]=1;
    for(k=1;k<=n-1;k++)
    {
        min=inf;
        for(i=1;i<=n;i++)
            if(book[i]==0&&dis[i]<min)
            {
                min=dis[i];
                u=i;
            }
        book[u]=1;
        sum+=dis[u];
        for(i=1;i<=n;i++)
            if(book[i]==0&&dis[i]>e[u][i])
                dis[i]=e[u][i];
    }    
    return sum;
}

动态规划

01背包

给你n种物品每种物品有一件和一个容量为m的背包
然后给你每种物品的体积和价值
求背包所能容下的最大价值
样例输入
3 8
4 3
3 2
2 1
样例输出
5
程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[110],w[110],dp[110];
int main()
{
    int i,j,n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
            scanf("%d%d",&v[i],&w[i]);
        for(i=1;i<=n;i++)
            for(j=m;j>=v[i];j--)
                dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        printf("%d\n",dp[m]);
    }
    return 0;
}

完全背包

给你n种物品每种物品有无穷多件和一个容量为m的背包
然后给你每种物品的体积和价值
求背包所能容下的最大价值

样例输入

3 8
4 3
3 2
2 1
样例输出
6
程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[110],w[110],dp[110];
int main()
{
    int n,m,i,j;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
            scanf("%d%d",&v[i],&w[i]);
        for(i=1;i<=n;i++)
            for(j=v[i];j<=m;j++)
                dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
        printf("%d\n",dp[m]);
    }
    return 0;
}

多重背包

给你n种物品每种物品有多件和一个容量为m的背包
然后给你每种物品的体积、价值和数量
求背包所能容下的最大价值

样例输入

3 10
4 3 3
3 2 2
2 1 1

样例输出

7

程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int v[110],w[110],c[110],dp[110];
int main()
{
    int n,m,i,j,k;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
            scanf("%d%d%d",&v[i],&w[i],&c[i]);
        for(i=1;i<=n;i++)
            for(j=1;j<=c[i];j++)
                for(k=m;k>=v[i];k--)
                    dp[k]=max(dp[k],dp[k-v[i]]+w[i]);
        printf("%d\n",dp[m]);
    }
    return 0;
}

最长公共子序列

样例输入:
2
asdf
adfsd
123abc
abc123abc
样例输出:
3
6
程序代码:

#include<stdio.h>
#include<string.h>
int dp[1010][1010];
int Max(int a,int b)
{
    if(a>b)
        return a;
    return b;
}
int main()
{
    int n,i,j,len1,len2;
    char str1[1010],str2[1010];
    scanf("%d",&n);
    while(n--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%s%s",str1,str2);
        len1=strlen(str1);
        len2=strlen(str2);
        for(i=1;i<=len1;i++)
            for(j=1;j<=len2;j++)
            {
                if(str1[i-1]==str2[j-1])
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j]=Max(dp[i-1][j],dp[i][j-1]);
            }
        printf("%d\n",dp[len1][len2]);
    }
    return 0;
}

单调递增子序列

 

#include<stdio.h>
#include<string.h>
char str[10010];
int dp[10010];
int main()
{
    int n,i,j,maxn,len;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s",str);
        len=strlen(str);
        for(i=0;i<len;i++)
            dp[i]=1;
        for(i=1;i<len;i++)
            for(j=0;j<i;j++)
                if(str[i]>str[j]&&dp[j]+1>dp[i])
                    dp[i]=dp[j]+1;
        maxn=dp[0];
        for(i=1;i<len;i++)
            if(dp[i]>maxn)
                maxn=dp[i];
        printf("%d\n",maxn);
    }
    return 0;
}

单调递增子序列(二分)

样例输入:
7
1 9 10 5 11 2 13
2
2 -1
样例输出:
5
1
程序代码:

#include<stdio.h>
#include<string.h>
int Two(int start,int end,int x);
int a[100010],dp[100010];
int main()
{
    int n,i,j,t,len;
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        for(i=0;i<n;i++)
            scanf("%d",&a[i]);
        dp[1]=a[0];
        len=1;
        for(i=1;i<n;i++)
        {
            t=Two(1,len,a[i]);//通过二分搜索查找a[i]要插入的位置    
            dp[t]=a[i];
            if(t>len)
                len=t;
        }
        printf("%d\n",len);
    }
    return 0;
}
int Two(int start,int end,int x)
{
    int mid;
    while(start<=end)
    {
        mid=(start+end)/2;
        if(x==dp[mid])
            return mid;
        if(x>dp[mid])
            start=mid+1;
        if(x<dp[mid])
            end=mid-1;
    }
    return start;
}

字符串匹配

KMP

Sample Input
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
Sample Output
1
3
0
题意描述:
找出a串中包含多少个b串
程序代码:

#include<stdio.h>
#include<string.h>
void get_next();
int next[10010],m,n;
char a[1000010],b[10010];
int main()
{
    int T,i,j,count;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s%s",b,a);
        n=strlen(a);
        m=strlen(b);
        get_next();
        i=0;
        j=0;
        count=0;
        while(i<n)
        {
            if(j==0&&a[i]!=b[j])
                i++;
            else if(j>0&&a[i]!=b[j])
                j=next[j-1];
            else
            {
                i++;
                j++;
            }
            if(j==m)
            {
                j=next[j-1];
                count++;
            }
        }
        printf("%d\n",count);
    }
    return 0;
}
void get_next()
{
    int i,j;
    i=1;
    j=0;
    next[0]=0;
    while(i<m)
    {
        if(j==0&&b[i]!=b[j])
        {
            next[i]=0;
            i++;
        }
        else if(j>0&&b[i]!=b[j])
            j=next[j-1];
        else
        {
            next[i]=j+1;
            i++;
            j++;
        }
    }
}

字典树

Sample Input

banana
band
bee
absolute
acm

ba
b
band
abc
Sample Output

2
3
1
0
题意描述:
给你多个单词,然后给你多个前缀,让你找具有该前缀的单词的个数,首先建立一个字典树,把所有的单词存里面,然后逐个输入前缀并逐个判断
程序代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
struct Trie{
    int v;
    Trie *next[26];
};
Trie root;
void get_trie(char *str);
int find_trie(char *str);
int main()
{
    int i,len;
    char str[15];
    for(i=0;i<26;i++)
        root.next[i]=NULL;
    while(fgets(str,15,stdin),str[0]!='\n')
    {
        len=strlen(str);//用fgets多了一个\n
        str[len-1]='\0';
        get_trie(str);
    }
    while(scanf("%s",str)!=EOF)
    {
        printf("%d\n",find_trie(str));
    }
    return 0;
}
void get_trie(char *str)
{
    int i,j,len,id;
    Trie *p=&root,*q;
    len=strlen(str);
    for(i=0;i<len;i++)
    {
        id=str[i]-'a';
        if(p->next[id]==NULL)
        {
            q=(Trie*)malloc(sizeof(root));
            q->v=1;
            for(j=0;j<26;j++)
                q->next[j]=NULL;
            p->next[id]=q;
            p=p->next[id];
        }
        else
        {
            p=p->next[id];
            p->v++;
        }
    }
}
int find_trie(char *str)
{
    int i,len,id;
    Trie *p=&root;
    len=strlen(str);
    for(i=0;i<len;i++)
    {
        id=str[i]-'a';
        p=p->next[id];
        if(p==NULL)
            return 0;
    }
    return p->v;
}

AC自动机

Sample Input
1
5
she
he
say
shr
her
yasherhs
Sample Output
3
题意描述:
给你n个单词和一篇文章,找出这n个单词在文章中共出现多少次
程序代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
using namespace std;
 
struct node{
    node *fail;
    node *next[26];
    int countn;
};
node *root;
char str[55];
char s[1000005];
 
void GetTrie(char *str);
void BuildAcAutomation();
int query();
 
int main()
{
    int len,i,T,n;
    scanf("%d",&T);
    while(T--)
    {
        root=new node();
        scanf("%d",&n);
        while(n--)
        {
            scanf("%s",str);
            GetTrie(str);
        }
        scanf("%s",s);
        BuildAcAutomation();
        printf("%d\n",query());
    }
    return 0;
}
void GetTrie(char *str)
{
    node *p;
    int i,id;
    p=root;
    for(i=0;str[i]!='\0';i++)
    {
        id=str[i]-'a';
        if(p->next[id]==NULL)
            p->next[id]=new node();
        p=p->next[id];
    }
    p->countn++;//代表单词数加1
    //printf("***%d\n",p->countn);
}
void BuildAcAutomation()
{
    int i;
    node *temp,*p;
    p=new node();
    queue<node*>q;
    q.push(root);
    while(!q.empty())
    {
        temp=q.front();
        q.pop();
        for(i=0;i<26;i++)
        {
            if(temp->next[i]!=NULL)
            {
                if(temp==root)
                    temp->next[i]->fail=root;
                else
                {
                    p=temp->fail;
                    while(p!=NULL)
                    {
                        if(p->next[i]!=NULL)
                        {
                            temp->next[i]->fail=p->next[i];
                            break;
                        }
                        p=p->fail;
                    }
                    if(p==NULL)
                        temp->next[i]->fail=root;
                }
                q.push(temp->next[i]);
            }
        }
    }
}
int query()
{
    int i,id,sum=0;
    node *p,*temp;
    p=root;
    for(i=0;s[i]!='\0';i++)
    {
        id=s[i]-'a';
        while(p->next[id]==NULL&&p!=root)
            p=p->fail;
        p=p->next[id];
        if(p==NULL)
            p=root;
        temp=p;
        while(temp!=root&&temp->countn!=0)
        {
            sum+=temp->countn;
            temp->countn=0;
            temp=temp->fail;
        }
    }
    return sum;
}

博弈

巴什博奕(Bash Game)

只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜
举一个最简单的例子就是,当n=m+1时,此时不管先手取多少,后手都能把剩下的取完,拓展到n等于m+1的倍数时,不管先手取多少,后手都可以取(m+1减去先手取的个数)个,最后先手一定会面临n=m+1的情况,此时先手必败,否则先手必胜
程序代码:

#include<stdio.h>
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n%(m+1)==0)  
            printf("先手必败\n");
           else
            printf("先手必胜\n");
    }
    return 0;
}

威佐夫博弈(Wythoff Game)

有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利

这有一个公式:t=(int)((a-b)*(1.0+sqrt(5.0))/2.0);让t与较小堆石子数相比,如果相等先手必败,否则先手必胜

程序代码:

#include<stdio.h>
#include<math.h>
int main()
{
    int a,b,t;
    while(scanf("%d%d",&a,&b)!=EOF)
    {
        if(a<b)  
        {
            a^=b;
            b^=a;
            a^=b;
        }
        t=(int)((a-b)*(1.0+sqrt(5.0))/2.0);
        if(t==b)
            printf("先手必败\n");
        else
            printf("先手必胜\n");    
    }
    return 0;
}

尼姆博弈(Nimm Game)

有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜
把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜
程序代码:

#include<stdio.h>
int main()
{
    int n,m,i,count;
    while(scanf("%d",&n)!=EOF)
    {
        count=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&m);
            count^=m;
        }
        if(count==0)  
            printf("先手必败\n");
        else
            printf("先手必胜\n");
    }
    return 0;
}

大数

浮点大数加法

#include<stdio.h>
#include<string.h>
int Max(int a,int b);
int main()
{
    char str1[410],str2[410];
    int max1[410],min1[410],max2[410],min2[410];
    int i,l1,l2,p,q,m,n;
    
    while(scanf("%s%s",str1,str2)!=EOF)
    {
        memset(max1,0,sizeof(max1));
        memset(max2,0,sizeof(max2));
        memset(min1,0,sizeof(min1));
        memset(min2,0,sizeof(min2));
        l1=strlen(str1);
        l2=strlen(str2);
        p=0;
        q=0;
        for(i=0;str1[i]!='\0';i++)
        {
    
            if(str1[i]=='.')
                break;
            else
                p++;
        }
        for(i=0;i<p;i++)
            max1[i]=str1[p-1-i]-'0';
        for(i=0;i<l1-1-p;i++)
            min1[i]=str1[p+1+i]-'0';
        for(i=0;str2[i]!='\0';i++)
        {
    
            if(str2[i]=='.')
                break;
            else
                q++;
        }
        for(i=0;i<q;i++)
            max2[i]=str2[q-1-i]-'0';
        for(i=0;i<l2-1-q;i++)
            min2[i]=str2[q+1+i]-'0';
        m=Max((l1-p-1),(l2-q-1));
        for(i=m-1;i>0;i--)
        {
            min1[i]+=min2[i];
            if(min1[i]>9)
            {
                min1[i]%=10;
                min1[i-1]++;
            }
        }
        min1[0]+=min2[0];
        if(min1[0]>9)
        {
            min1[0]%=10;
            max1[0]++;
        }
        n=Max(p,q);
    
        for(i=0;i<n;i++)
        {
            max1[i]+=max2[i];
            if(max1[i]>9)
            {
                max1[i]%=10;
                max1[i+1]++;
            }
        }
        if(max1[n]>0)
            for(i=n;i>=0;i--)
                printf("%d",max1[i]);
        else
            for(i=n-1;i>=0;i--)
                printf("%d",max1[i]);
        while(1)
        {
            if(min1[m-1]==0)
                m--;
            else
                break;
        }
        if(m>0)
            printf(".");
        for(i=0;i<m;i++)
            printf("%d",min1[i]);
        printf("\n");
    }
    return 0;
}
 
int Max(int a,int b)
{
    if(a>b)
        return a;
    else
        return b;
}

大数乘法

#include<stdio.h>
#include<string.h>
int main()
{
    char str1[110],str2[110];
    int a[110],b[110],c[220],d[220];
    int len1,len2,i,j,k,t;
    scanf("%s%s",str1,str2);
    len1=strlen(str1);
    len2=strlen(str2);
    for(i=0;i<len1;i++)
        a[i]=str1[len1-1-i]-'0';
    for(i=0;i<len2;i++)
        b[i]=str2[len2-1-i]-'0';
    memset(c,0,sizeof(c));
    for(i=0;i<len1;i++)
        for(j=0;j<len2;j++)
            c[i+j]+=a[i]*b[j];
    t=j=0;
    for(i=0;i<len1+len2-1;i++)
    {
        d[j++]=(c[i]+t)%10;
        t=(c[i]+t)/10;
    }
    while(t)
    {
        d[j++]=t%10;
        t/=10;
    }
    for(i=j-1;i>=0;i--)
        printf("%d",d[i]);
    return 0;
}

大数开方

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define DEPTH 10
 
typedef int BigInteger[10100];
 
int comp(const BigInteger a,const int c,const int d,const BigInteger b) //大数比较
{
    int i,t=0,O=-DEPTH*2;
    if(b[0]-a[0]<d&&c) return 1;
    for(i=b[0];i>d;i--)
    {
        t=t*DEPTH+a[i-d]*c-b[i];
        if(t>0) return 1;
        if(t<O) return 0;
    }
    for(i=d;i;i--)
    {
        t=t*DEPTH-b[i];
        if(t>0) return 1;
        if(t<O) return 0;
    }
    return t>0;
}
 
void sub(BigInteger a,const BigInteger b,const int c,const int d) //大数减
{
    int i,O=b[0]+d;
    for(i=1+d;i<=O;i++)
        if((a[i]-=b[i-d]*c)<0)
            a[i+1]+=(a[i]-DEPTH+1)/DEPTH,a[i]-=(a[i]-DEPTH+1)/DEPTH*DEPTH;
    for(;a[i]<0;a[i+1]+=(a[i]-DEPTH+1)/DEPTH,a[i]-=(a[i]-DEPTH+1)/DEPTH*DEPTH,i++);
    for(;!a[a[0]]&&a[0]>1;a[0]--);
}
 
void Sqrt(BigInteger b,BigInteger a) //开平方
{
    int h,l,m,i;
    memset((void*)b,0,sizeof(BigInteger));
    for(i= b[0]=(a[0]+1)>>1;i;sub(a,b,m,i-1),b[i]+=m,i--)
        for(h=DEPTH-1,l=0,b[i]=m=(h+l+1)>>1;h>l;b[i]=m=(h+l+1)>>1)
            if(comp(b,m,i-1,a)) h=m-1;
            else l = m;
    for(;!b[b[0]]&&b[0]>1;b[0]--);
    for (i = 1; i <= b[0]; b[i++] >>= 1);
}
 
char str[10100];
BigInteger a,b;
 
int main()
{
    while(scanf("%s",str)!=EOF)
    {
         a[0]=strlen(str);
        for(int i=1; i<=a[0]; i++)
            a[i]=str[a[0]-i]-'0';
        Sqrt(b,a);
        for(int i=b[0]; i>=1; i--)
            printf("%d",b[i]);
        printf("\n");
 
    }
    return 0;
}

Hash(除留余数法+链地址法)

#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int N=10;

struct Node{
    int num;
    Node *next;
};
struct HashTable{
    Node *element[N];
    int countn;
};

HashTable *p;

void InsertHashTable(int key);
int Search(int key);

int main()
{
    int i,n;
    int a[N]={12,45,2,6,78,9,0,1,15,18};
    p=new HashTable();//这个先是初始化p这个头指针
    for(i=0;i<N;i++)
        p->element[i]=new Node();//这是初始化p下的每个element指针

    for(i=0;i<N;i++)
        InsertHashTable(a[i]);
    while(scanf("%d",&n)!=EOF)//可输入待查找元素
    {
        printf("%d\n",Search(n));
    }
    return 0;
}
void InsertHashTable(int key)
{
    Node *q;
    q=new Node();
    q->num=key;
    q->next=p->element[key%N]->next;
    p->element[key%N]->next=q;
}
int Search(int key)
{
    Node *q;
    q=new Node();
    q=p->element[key%N]->next;
    while(q!=NULL)
    {
        if(q->num==key)
            return key%N;
        q=q->next;
    }
    return -1;
}

堆排序(最小堆)

该排序首先要建立堆以建立最小堆为例,在建立堆的时候用到了向下调整的思想,即该点如果的值a[i],如果小于a[2*i]或a[2*+1],则需要把a[i]与a[2*i]和a[2*+1]的较小者交换,以达到上小下大,需要注意的是在建立最小堆的时候要从后往前建立,也就是最后一个非叶子节点开始建立,即a[n/2],因为这样可以达到下面的数都比上面的数大(如果从根开始建堆的话,可能会出现最下面的数很小,但是中间的数较大,然后中上的数又较小,而最上面的数很大,这样一来,最上面的数与中上换过之后不能与中间的数交换,以导致最下面的最小数无法换到上面,下面给的样例就是这样的)

样例输入:
14
99 5 36 7 22 17 46 12 2 19 25 28 1 92
样例输出:
1 2 5 7 12 17 19 22 25 28 36 46 92 99
程序代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
int a[110];
int n;
void siftdown(int i);//向下调整
int main()
{
    int i,j,maxn=-1;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]>maxn)
            maxn=a[i];
    }
    for(i=n/2;i>=1;i--)//建立堆
        siftdown(i);
 
    for(i=1;i<=n;i++)
    {
        printf("%d ",a[1]);
        a[1]=maxn;
        siftdown(1);
    }
    printf("\n");
    return 0;
}
void siftdown(int i)//最小堆
{
    int t,flag=0,temp;
    
    while(2*i<=n&&flag==0)
    {
        t=i;
        if(a[t]>a[2*i])
            t=2*i;
        if(2*i+1<=n&&a[t]>a[2*i+1])
            t=2*i+1;
        if(t!=i)
        {
            temp=a[i];
            a[i]=a[t];
            a[t]=temp;
            i=t;
        }
        else
            flag=1;
    }
}

拓扑排序

每次都是把入度为0的点输出,当然输出之后会更新其他点的入度
样例
6 8
a b
a c
a d
c b
c d
d e
f d
f e
输出
a c b f d e
程序代码:

#include<stdio.h>
#include<queue>
using namespace std;
 
int a[110][110],b[110];
int main()
{
    priority_queue<int,vector<int>,greater<int> >q;//greater<int> 后有一个空格
    char x,y;
    int A;
    int i,n,m;
    scanf("%d%d",&n,&m);
    for(i=0;i<m;i++)
    {
        scanf(" %c %c",&x,&y);
        a[x-'a'][y-'a']++;
        b[y-'a']++;
    }
 
    for(i=0;i<n;i++)
        if(b[i]==0)
            q.push(i);
 
    while(!q.empty())
    {
        A=q.top();
        q.pop();
        printf("%c ",A+'a');
        for(i=0;i<n;i++)
            if(a[A][i]>0)
            {
                b[i]--;
                if(b[i]==0)
                    q.push(i);
            }
    }
    return 0;
}

归并排序

#include<stdio.h>
int a[110],temp[110];
 
void MergeSort(int l,int r);
void Merge(int l,int mid,int r);
 
int main()
{
    int n,i;
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&a[i]);
    MergeSort(0,n-1);
 
    for(i=0;i<n;i++)
        printf("%d ",a[i]);
    return 0;
}
void MergeSort(int l,int r)
{
    int mid;
    if(l<r)
    {
        mid=(l+r)/2;
        MergeSort(l,mid);
        MergeSort(mid+1,r);
        Merge(l,mid,r);
    }
}
void Merge(int l,int mid,int r)
{
    int i,j,k;
    i=l;
    j=mid+1;
    k=l;
    while(i<=mid&&j<=r)
    {
        if(a[i]>a[j])
            temp[k++]=a[j++];
        else
            temp[k++]=a[i++];
    }
    while(i<=mid)
        temp[k++]=a[i++];
    while(j<=r)
        temp[k++]=a[j++];
    for(i=l;i<=r;i++)
        a[i]=temp[i];
}

二分匹配

二分匹配举个简单的例子,有m个女生n个男生k组关系代表着该女生和该男生可以在一起,目的是达到最大的匹配对数,可以用二分匹配来找最大匹配数,大致过程就是从第一个女生开始找,找到一个与她有关系的男生后,开始找下一个女生,如果下一个女生也与该男生有关系,那么就让该女生找其他的男生,如果找到了那么该女生就把该男生让给下一个女生,如果找不到,那么下一个女生就去找下一个男生,一直这样把所有的女生都找一遍,即可得到最大匹配数
样例输入:
3 3 4
1 2
1 3
2 1
2 2
样例输出:
2
程序代码:

#include<stdio.h>
#include<string.h>
int book[110],e[110][110],match[110],m,n,k;
int dfs(int u);
int hungry();
int main()
{
    int i,j,a,b;
    while(scanf("%d%d%d",&m,&n,&k)!=EOF)
    {
        memset(e,0,sizeof(e));
        for(i=1;i<=k;i++)
        {
            scanf("%d%d",&a,&b);
            e[a][b]=1;
        }
        printf("%d\n",hungry());
    }    
    return 0;
}
int dfs(int u)
{
    int i;
    for(i=1;i<=n;i++)
        if(book[i]==0&&e[u][i]==1)
        {
            book[i]=1;
            if(match[i]==0||dfs(match[i])==1)
            {
                match[i]=u;
                return 1;
            }
        }
    return 0;
}
int hungry()
{
    int i,sum=0;
    memset(match,0,sizeof(match));
    for(i=1;i<=m;i++)
    {
        memset(book,0,sizeof(book));
        if(dfs(i)==1)
            sum++;
    }
    return sum;
}

并查集

n个强盗,m表示警方的线索,接下来m行,每行两个整数a,b表示a强盗和b强盗是同伙,问一共有多少个团伙
样例输入:
10 9
1 2
3 4
5 2
4 6
2 6
8 7
9 7
1 6
2 4
样例输出:
3

#include<stdio.h>
int getf(int v);
void merge(int u,int v);
int f[110];
int main()
{
    
    int i,n,m,x,y,sum=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        f[i]=i;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        merge(x,y);
    }
    for(i=1;i<=n;i++)
        if(f[i]==i)
            sum++;
    printf("%d\n",sum);
    return 0;
}
int getf(int v)
{
    if(v==f[v])
        return v;
    f[v]=getf(f[v]);
    return f[v];
}
void merge(int u,int v)
{
    u=getf(u);
    v=getf(v);
    if(u!=v)
        f[v]=u;
    return;
}

最大流

首先是bfs,这是对整个图进行分层,默认后一层等于前一层加1
然后就是dfs,每次搜索,因为之前对图已经分层,所以直接可以按层进行深搜,直到找到n为止
最后就是Dinic了,每次增广找到最短的一条边,并且把所有的正向边减少a反向边增加a
样例输入:
6 7
1 2 2
1 3 1
2 5 1
2 4 2
3 4 1
4 6 2
5 6 1
样例输出:
3
程序代码:

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
const int inf=1e9;

int n,m;
int e[110][110];
int dis[110];
int bfs(int s);
int Find(int x,int minn);
int Dinic();

int main()
{
    int i,u,v,w;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(e,0,sizeof(e));

        for(i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            e[u][v]=w;
        }
        printf("%d\n",Dinic());
    }
    return 0;
}

int bfs(int s)
{
    int A,i;
    memset(dis,-1,sizeof(dis));
    queue<int>q;
    q.push(s);
    dis[1]=0;

    while(!q.empty())
    {
        A=q.front();
        q.pop();
        for(i=1;i<=n;i++)
        {
            if(dis[i]==-1&&e[A][i]>0)
            {
                dis[i]=dis[A]+1;
                q.push(i);
            }
        }
    }
    if(dis[n]>0)
        return 1;
    return 0;
}

int dfs(int x,int minn)
{
    int i,a=0;
    if(x==n)
        return minn;
    for(i=1;i<=n;i++)
    {
        if(e[x][i]>0&&dis[i]==dis[x]+1&&(a=dfs(i,min(minn,e[x][i]))))
        {
            e[x][i]-=a;
            e[i][x]+=a;
            return a;
        }
    }
    return 0;
}

int Dinic()
{
    int temp,sum=0;
    while(bfs(1))
    {
        while(temp=dfs(1,inf))
        {

            sum+=temp;
        }
    }
    return sum;
}

欧拉函数

//欧拉函数是小于n的正整数中与n互质的数的数目
#include<stdio.h>
#include<stdlib.h>
int eular(int n);

int main ()
{
      int n;
      scanf("%d",&n);
      printf("%d",eular(n));
      return 0;
}
int eular(int n)
{
    int ret=1,i;
    for(i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            n/=i,ret*=i-1;
            while(n%i==0) n/=i,ret*=i;
        }
    }
    if(n>1)
        ret*=n-1;
    return ret;
}

扩展欧几里得

已知a, b求解一组x,y,ax+by = gcd(a, b) =d(解一定存在)
gcd(a,b)=gcd(b,a)=gcd(-a,b)=gcd(|a|,|b|)
扩展欧几里得求逆元
什么是逆元

当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:

设c是b的逆元,则有b*c≡1(mod m);

则(a/b)%m = (a/b)*1%m = (a/b)*b*c%m = a*c(mod m);

即a/b的模等于a*b的逆元的模;
A/B
Problem Description
要求(A/B)%9973,但由于A很大,我们只给出n(n=A%9973)(我们给定的A必能被B整除,且gcd(B,9973) = 1)。

Input
数据的第一行是一个T,表示有T组数据。
每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。

Output
对应每组数据输出(A/B)%9973。

Sample Input
2
1000 53
87 123456789

Sample Output
7922
6060

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-a/b*y;
    return d;
}
ll inv(ll a,ll m){
    ll x,y;
    ll d=exgcd(a,m,x,y);
    if(d==1){
        //处理负数
        return (x%m+m)%m;
    }
    return -1;
}
ll n,b;
int t;
const ll MOD=9973;
int main(void){
    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld",&n,&b);
        ll c=inv(b,MOD);
        printf("%lld\n",n*c%MOD);
    }
    return 0;
}

费马小定理

p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)
莫比乌斯函数
莫比乌斯函数,由德国数学家和天文学家莫比乌斯提出。梅滕斯(Mertens)首先使用μ(n)(miu(n))作为莫比乌斯函数的记号。(据说,高斯(Gauss)比莫比乌斯早三十年就曾考虑过这个函数)。
具体定义如下:
如果一个数包含平方因子,那么miu(n) = 0。例如:miu(4), miu(12), miu(18) = 0。
如果一个数不包含平方因子,并且有k个不同的质因子,那么miu(n) = (-1)^k。例如:miu(2), miu(3), miu(30) = -1,miu(1), miu(6), miu(10) = 1。
给出一个数n, 计算miu(n)。
输入
输入包括一个数n,(2 <= n <= 10^9)
输出
输出miu(n)。
输入样例
5
输出样例
-1

#include<stdio.h>
int num;
int miu(int n)
{
    int i;
    for(i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            n=n/i;
            num++;
            if(n%i==0)
                return 0;
        }
    }
    num++;
    if(num%2==0)
        return 1;
    return -1;
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
           num=0;
           printf("%d\n",miu(n));    
    }
    return 0;
}


题意:对于 a, b, c, d, k . 有 x 属于 [a, b],  y 属于 [c, d], 求 gcd(x, y) = k 的 x, y 的对数 . 可以假设在所有测试用例中a = c = 1。
注意: (x, y), (y, x) 算一种情况 .

#include <iostream>
#include <stdio.h>
#include <string.h>
#define ll long long
using namespace std;

const int MAXN = 1e6 + 10;

bool check[MAXN];
int mu[MAXN], prime[MAXN];

void Moblus(void){
    memset(check, false, sizeof(check));
    int tot = 0;
    mu[1] = 1;
    for(int i = 2; i < MAXN; i++){
        if(!check[i]){
            prime[tot++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < tot && i * prime[j] < MAXN; j++){
            check[i * prime[j]] = true;
            if(i % prime[j] == 0){
                mu[i * prime[j]] = 0;
                break;
            }else mu[i * prime[j]] = -mu[i];
        }
    }
}

int main(void){
    int t, a, b, c, d, k;
    Moblus();
    scanf("%d", &t);
    for(int i = 1; i <= t; i++){
        scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
        if(k == 0){
            printf("Case %d: 0\n", i);
            continue;
        }
        if(b > d) swap(b, d);
        b /= k;
        d /= k;
        ll ans1 = 0, ans2 = 0;
        for(int j = 1; j <= b; j++){
            ans1 += (ll)mu[j] * (b / j) * (d / j);
        }
        for(int j = 1; j <= b; j++){
            ans2 += (ll)mu[j] * (b / j) * (b / j);
        }
        printf("Case %d: %lld\n",i, ans1 - (ans2 >> 1));
    }
    return 0;
}