模板总结

97 篇文章 0 订阅
93 篇文章 1 订阅

1.快速幂模板

常规的求幂方法一般难一些的题都会超时,这里给出模板

假如x为底,y为指数

int mi(int x,int y)
{
    int t=1;
    while(y!=0)
    {
        if(y%2==1)
            t*=x;
        x=x*x;
        y=y/2;
    }
    return t;
}


假如要%上一个数,假如要模的数为mod,写法

int mod=1e5+7;  
long long mi(long long x,long long y)  
{  
    long long t=1;  
    x=x%mod;  
    while(y!=0)  
    {  
        if(y%2==1)  
            t=(t*x)%mod;  
        x=(x*x)%mod;  
        y=y/2;  
    }  
    return t;  
} 

这里long long防爆

矩阵快速幂例题:http://blog.csdn.net/qq_37497322/article/details/77778141

2.并查集寻根函数+路径压缩写法

使用时要先初始化pre数组指向自己

int find(int x)
{
    if(x!=pre[x])
        pre[x]=find(pre[x]);
    return pre[x];
}

3.筛法求素数打表+顺便求欧拉函数

ps:欧拉函数:euler(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…(1-1/pn),其中p1,p2……pn为小于等于x的素数,设n为正整数,以 φ(n)表示不超过n且与n互素的正整数的个数,称为n的欧拉函数值,若n为质数则欧拉函数值为n-1

void ini() 
{  
    int i,j,k=0;  
    memset(vis,0,sizeof(vis));  
    memset(b,0,sizeof(b));  
    for(i=2;i<=N;i++)//N为数据范围  
    {  
        if(!vis[i])  
        {  
            a[k]=i; //数组a中就是素数 
            k++;  
            for (j=i*i;j<=N;j+=i)  
            {  
                if(!vis[j])  
                    b[j]=j;  
                b[j]=b[j]-b[j]/i;//打表的同时完成求欧拉函数  
                vis[j]=1;  
            }  
        }  
    }  
}
4.欧几里得求最大公约数

最小公倍数为两数相乘除最大公约数

int ojld(int x,int y)
{
    int r=x%y;
    while(r!=0)
    {
        x=y;
        y=r;
        r=x%y;
    }
    return y;
}
5.对结构体排序写法||优先队列建最大最小堆写法

假如定义一个结构体

struct node  
{  
    int p1,p2;  
};  
node a[105];
要按照优先级值p1按照从小到大排序,如果相同就按p2从小到大排序

那么这样写cmp函数

int cmp(node x,node y)
{
    if(x.p1!=x.p1)
        return x.p1<y.p1;
    else
        return x.p2<x.p2;
}
然后sort(a,a+n,cmp)

优先队列写法

struct node  
{  
    int v; //值
    int p; //优先级
    friend bool operator<(const node& x,const node& y)//建一个最大堆,即优先级大的先弹出
    {  
        return x.p<y.p;  
    }  
    friend bool operator>(const node& x,const node& y)//建一个最小堆,即优先级小的先弹出 
    {  
        return x.p>y.p;  
    }  
};  
priority_queue<node,vector<node>,less<node> >q1; //less表示使用重载的小于号 
priority_queue<node,vector<node>,greater<node> >q2;  //greater表示重载的大于号
6.有关各种stl容器用法
见之前写的文章 点击打开链接

7.母函数模板

//为计算结果,b为中间结果。
int a[MAX],b[MAX];
//初始化a
memset(a,0,sizeof(a));
a[0]=1;
for (int i=1;i<=17;i++)//循环每个因子
{
	memset(b,0,sizeof(b));
	for (int j=n1[i];j<=n2[i]&&j*v[i]<=P;j++)//循环每个因子的每一项
		for (int k=0;k+j*v[i]<=P;k++)//循环a的每个项
			b[k+j*v[i]]+=a[k];//把结果加到对应位
	memcpy(a,b,sizeof(b));//b赋值给a
}

K对应具体问题中物品的种类数。

v[i]表示该乘积表达式第i个因子的权重,对应于具体问题的每个物品的价值或者权重。

n1[i]表示该乘积表达式第i个因子的起始系数,对应于具体问题中的每个物品的最少个数,即最少要取多少个。

n2[i]表示该乘积表达式第i个因子的终止系数,对应于具体问题中的每个物品的最多个数,即最多要取多少个。

P是可能的最大指数。拿钞票组合这题来说,如果要求15元有多少组合,那么P就是15;如果问最小的不能拼出的数值,那么P就是所有钱加起来的和

如果n2是无穷,那么第二层循环条件j<=n2[i]可以去掉。

最后结果为a[p];

附上传送例题: 点击打开链接

8.dp经典之背包问题模板

01背包,即每种东西只有一样,p[s]为答案

for(i=0;i<n;i++)//n为物品数目
{
    for(j=s;j>=v[i];j--)//v为物品占的背包容量,s为背包总容量
    {
        p[j]=max(p[j],p[j-v[i]]+w[i])//w为物品价值
     }
}

 

完全背包,即每种东西有无数种,p[s]为答案

for(i=0;i<n;i++)//n为物品数目
{
    for(j=v[i];j<=s;j++)//v为物品占的背包容量,s为背包总容量
    {
        p[j]=max(p[j],p[j-v[i]]+w[i])//w为物品价值
     }
}

 

关于数据压缩例题传送门:点击打开链接

9.最短路算法

单源最短路算法:

bellman-ford(处理负权边)传送门:点击打开链接

迪杰斯特拉算法(最常用)传送门:点击打开链接

SPFA(可以求负环,类似于bfs算法,有时用起来有奇效)传送门:点击打开链接

图中所有最短路算法:弗洛伊德(无视负权边的无敌算法。。就是N^3爆炸)

for(k=1;k<=n;k++)//开始时要讲p中所有边置为INF最大值
{  
     for(i=1;i<=n;i++)  
     {  
          for(j=1;j<=n;j++)  
          {  
              if(p[i][k]+p[k][j]<p[i][j])  
              {  
                   p[i][j]=p[i][k]+p[k][j];  
              }  
          }  
     }  
 }


10.最小生成树模板(例题理解)

克鲁斯卡尔算法(适用于稀疏图,即点较多边较少的图)贪心思路

例题传送:点击打开链接

prim算法(适用于稠密图,即点较少边较多的图,一般用优先队列优化)

例题传送:点击打开链接
11.常用高精度

大数加法

      int i,j,len1,len2,len3,a[105],b[105],c[105],w;
    char s1[105],s2[105];
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        len1=strlen(s1);
        len2=strlen(s2);
        for(i=len1-1,j=0;i>=0;i--,j++)
            a[j]=s1[i]-'0';
        for(i=len2-1,j=0;i>=0;i--,j++)
            b[j]=s2[i]-'0';
        w=0;
        len3=0;
        for(i=0;i<max(len1,len2);i++)
        {
            c[i]=a[i]+b[i]+w;
            w=c[i]/10;
            c[i]%=10;
            len3++;
        }
        while(w)
        {
            c[len3]=w;
            w=c[len3]/10;
            c[len3]%=10;
            len3++;
        }
        for(i=len3-1;i>=0;i--)
            printf("%d",c[i]);
        printf("\n");
    }


关于大数减法:一定调整到大的减去小的然后借位就好了

大数乘法:

int i,j,len1,len2,len3,a[105],b[105],c[205],w;
    char s1[105],s2[105];
    while(scanf("%s%s",s1,s2)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        len1=strlen(s1);
        len2=strlen(s2);
        for(i=len1-1,j=0;i>=0;i--,j++)
            a[j]=s1[i]-'0';
        for(i=len2-1,j=0;i>=0;i--,j++)
            b[j]=s2[i]-'0';
        for(i=0;i<len1;i++)
        {
            w=0;
            for(j=0;j<len2;j++)
            {
                c[i+j]+=(a[i]*b[j]+w);
                w=c[i+j]/10;
                c[i+j]%=10;
            }
            len3=i+j;
            while(w)
            {
                c[len3]+=w;
                w=c[len3]/10;
                c[len3]%=10;
                len3++;
            }
        }
        int tag=0;
        for(i=len3-1;i>=0;i--)
        {
            if(!c[i]&&!tag)//防止输入时输入前导0
                continue;
            tag=1;
            printf("%d",c[i]);
        }
        if(!tag)
            printf("0");
        printf("\n");
    }

附上超好用的大数运算模板

class bigInt
{
public:
    int num[302], len;
    bigInt() { num[0] = 0, len = 0; }
    bigInt operator=(const int &a)
    {
        int tmp = a;
        len = 0;
        while (tmp)
            num[len++] = tmp % 10, tmp /= 10;
        if (!len) num[0] = 0, len = 1;
    }
    bigInt(const int &a)
    {
        int tmp = a;
        len = 0;
        while (tmp)
            num[len++] = tmp % 10, tmp /= 10;
        if (!len) num[0] = 0, len = 1;
    }
    friend bool operator<(const bigInt &b,const bigInt &a)
    {
        if (a.len !=b.len)
            return b.len < a.len;
        for (int i =b.len - 1; i >= 0; i--)
            if (b.num[i] != a.num[i])
                return b.num[i] < a.num[i];
        return false;
    }
    friend bool operator>(const bigInt & b,const bigInt &a)
    {
        if (a.len !=b.len)
            return b.len > a.len;
        for (int i = b.len - 1; i >= 0; i--)
            if (b.num[i] != a.num[i])
                return b.num[i] > a.num[i];
        return false;
    }
    bigInt operator+(const bigInt &a)
    {
        bigInt res;
        int i, j, c = 0, adda, addb;
        for (i = 0, j = 0; i < len || j < a.len || c; )
        {
            adda = 0, addb = 0;
            if (i < len)
                adda = num[i++];
            if (j < a.len)
                addb = a.num[j++];
            res.num[res.len++] = (adda + addb + c) % 10;
            c = (adda + addb + c) / 10;
        }
        return res;
    }
    bigInt operator-(const bigInt &b)
    {
        bigInt res;
        int i, j, c = 0, suba, subb;
        for (i = 0, j = 0; i < len || j < b.len || c; )
        {
            suba = 0, subb = 0;
            if (i < len)
                suba = num[i++];
            if (j < b.len)
                subb = b.num[j++];
            res.num[res.len++] = (suba - subb + c + 10) % 10;
            c = (suba - subb + c + 10) / 10 - 1;
        }
        for (i = res.len - 1; i > 0; i--)
            if (res.num[i]) break;
        res.len = i + 1;
        return res;
    }
    bigInt operator*(const bigInt &b)
    {
        bigInt res;
        int i, j, c, now, mulb, tmp;
        memset(res.num, 0, sizeof(int) * (len + b.len));
        for (i = 0; i < len; i++)
        {
            now = i, c = 0;
            for (j = 0; j < b.len || c; )
            {
                mulb = 0;
                if (j < b.len)
                    mulb = b.num[j++];
                tmp = res.num[now] + num[i] * mulb + c;
                res.num[now++] = tmp % 10;
                c = tmp / 10;
            }
        }
        for (i = len + b.len - 1; i > 0; i--)
            if (res.num[i]) break;
        res.len = i + 1;
        return res;
    }
    bigInt operator/(const bigInt &b)
    {
        bigInt res, diva;
        int i, j, c;
        for (i = len - 1; i >= 0; i--)
        {
            if (diva.len > 1 || diva.num[0])
            {
                for (j = diva.len - 1; j >= 0; j--)
                    diva.num[j + 1] = diva.num[j];
                diva.len++;
            }
            diva.num[0] = num[i];
            if (!diva.len) diva.len = 1;
            res.num[i] = 0;
            while (!(diva < b))
                diva = diva - b, res.num[i]++;
        }
        for (i = len - 1; i > 0; i--)
            if (res.num[i]) break;
        res.len = i + 1;
        return res;
    }
    bigInt operator%(const bigInt &b)
    {
        bigInt res, diva;
        int i, j, c;
        for (i = len - 1; i >= 0; i--)
        {
            if (diva.len > 1 || diva.num[0])
            {
                for (j = diva.len - 1; j >= 0; j--)
                    diva.num[j + 1] = diva.num[j];
                diva.len++;
            }
            diva.num[0] = num[i];
            if (!diva.len) diva.len = 1;
            res.num[i] = 0;
            while (!(diva < b))
                diva = diva - b, res.num[i]++;
        }
        for (i = diva.len - 1; i > 0; i--)
            if (diva.num[i]) break;
        diva.len = i + 1;
        return diva;
    }
    void display()
    {
        int i;
        for (i = len - 1; i > 1; i--)
            if (num[i]) break;
        for (; i >= 0; i--)
            printf("%d", num[i]);
        printf("\n");
    }
};


12.线段树有关

线段树详解:点击打开链接

线段树的单点更新+查询:点击打开链接

线段树的区间更新+查询+lazy tag:点击打开链接

线段树的区间合并(添加信息使其符合加法法则):点击打开链接

线段树扫描线+离散化:点击打开链接

树状数组(适用于单点更新,区间求和):点击打开链接

13.求逆序对

最常用的是归并排序

这里有树状数组求逆序对详解(可以求单点的逆序对数):点击打开链接

下面是归并排序求总逆序对数:

int a[5005],n,ans;  
void Merge(int l,int r,int mid)  
{  
    int t[5005],i=l,j=mid+1,k=l;  
    while(i<=mid&&j<=r)  
    {  
        if(a[i]>a[j])  
        {  
            t[k]=a[j];  
            ans+=mid-i+1;  
            k++;  
            j++;  
        }  
        else  
        {  
            t[k]=a[i];  
            i++;  
            k++;  
        }  
    }  
    while(i<=mid)  
    {  
        t[k]=a[i];  
        k++;  
        i++;  
    }  
    while(j<=r)  
    {  
        t[k]=a[j];  
        k++;  
        j++;  
    }  
    for(i=l;i<=r;i++)  
        a[i]=t[i];  
}  
void guibin(int l,int r)  
{  
    if(l>=r)  
        return;  
    int mid=(l+r)/2;  
    guibin(l,mid);  
    guibin(mid+1,r);  
    Merge(l,r,mid);  
} 

14.各种子序列

常见的有两种,最长上升子序列和最长公共子序列


最长上升子序列:

要用两个一维数组,一个记录到该节点时能够得到的最长长度,和得到该长度的结尾值,每次思路就是往前面查找已经扫过的记录的最长的且结尾值小于当前值的一个节点,然后更新此处长度为找到的长度+1,同时更新结尾值。


最长公共子序列:

用一个二维数组,dp[i][j],二重循环扫从开头两个串,注意串如果是从0开始,则i,j从1开始,那么如果s1[i-1]==s2[j-1],则dp[i][j]=dp[i-1][j-1]+1,不等则dp[i][j]=max(dp[i-1][j],dp[i][j-1]);

假如长度分别是n,m,dp[n][m]就是答案

严格的最长上升子序列nlogn算法

#include<stdio.h>
int num[1000],temp[1000];
int top;
int binary_search(int x)
{
    int l=0,r=top,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(temp[mid]>=x) r=mid-1;
        else //temp[mid]<x
        {
            l=mid;
            if(temp[l+1]>=x)return l+1;
            else l++;
        }
    }
}//找到序列中不大于x的最大的数,正因为二分查找才使得复杂度变身为n*log(n) 
int main()
{
    int n,i;
    while(scanf("%d",&n)==1)
    {
        for(i=0;i<n;i++)
            scanf("%d",&num[i]);
        top=0;temp[0]=num[0];
        for(i=1;i<n;i++)
        {
            if(num[i]>temp[top]) temp[++top]=num[i];
            else
            {
                if(num[i]<=temp[0]) temp[0]=num[i]; //算是
                else  temp[binary_search(num[i])]=num[i];
            }
        }
        printf("%d\n",top+1);
    }
    return 0;
}

然后是最长不下降子序列nlogn算法

const int N = 30001;   
int  a[N], f[N], d[N];  // f[i]用于记录 a[0...i]的最大长度   
int bsearch(const int *f, int size, const int &a)   
{   
    int  l=0, r=size-1;   
    while( l <= r )  
    {   
        int  mid = (l+r)>>1;   
        if( a >= d[mid-1] && a < d[mid] )   
            return mid;             // >&&<= 换为: >= && <   
        else if( a <d[mid] )   
                r = mid-1;   
        else l = mid+1;   
    }   
}   
int LIS(const int *a, const int &n)  
{   
    int  i, j, size = 1;   
    d[0] = a[0]; f[0] = 1;   
    for( i=1; i < n; ++i )  
    {   
        if( a[i] < d[0] )             // <= 换为: <   
            j = 0;                 
        else if( a[i] >= d[size-1] ) // > 换为: >=    
            j = size++;           
        else   
            j = bsearch(d, size, a[i]);   
  
        d[j] = a[i]; f[i] = j+1;   
    }   
    return size;   
}   
int main()  
{   
    int  i, n;   
    while( scanf("%d",&n) != EOF )  
    {   
        for( i=0; i < n; ++i )   
            scanf("%d", &a[i]);   
  
        printf("%d/n",LIS(a, n));// 求最大递增/上升子序列(如果为最大非降子序列,只需把上面的注释部分给与替换)   
    }   
    return 0;   
}  


15.二分图匹配

匈牙利算法模板

见博客http://blog.csdn.net/qq_37497322/article/details/76788273

详细了解二分图匹配:http://blog.csdn.net/qq_37497322/article/details/76850009
HK算法,比匈牙利算法优化得多的算法http://blog.csdn.net/qq_37497322/article/details/77036433

16.KMP模板

预处理求next数组

const int N = 1000002;
int Next[N];//存匹配值
char S[N], T[N];//s为要查找的母字符串,T为模板串
int slen, tlen;
void init()
{
    int i,j;
    i=0,j=-1;
    Next[0]=-1;
    while(T[i])
    {
        if(j==-1||T[i]==T[j])
        {
            i++;
            j++;
            Next[i]=j;
        }
        else
            j=Next[j];
    }
}

KMP函数:查找T在S中出现的次数

int KMP()
{
    int ans = 0;
    int i, j = 0;
    if(slen==1&&tlen==1)
    {
        if(S[0] == T[0])
            return 1;
        else
            return 0;
    }
    init();
    for(i=0;i<slen;i++)
    {
        while(j>0&&S[i]!=T[j])
            j=Next[j];
        if(S[i]==T[j])
            j++;
        if(j==tlen)
        {
            ans++;
            j=Next[j];
        }
    }
    return ans;
}
返回T在S中第一次出现的位置写法

返回模式串T在主串S中首次出现的位置
返回的位置是从0开始的。
*/
int KMP_Index()
{
    int i=0,j=0;
    init();
    while(i<slen&&j<tlen)
    {
        if(j==-1||S[i]==T[j])
        {
            i++;j++;
        }
        else
            j=Next[j];
    }
    if(j==tlen)
        return i-tlen;
    else
        return -1;
}

最大/最小表示法模板

int getmin(int flag,char T[])//flag里面0为最小表示法,1为最大表示法
{
    int i=0,j=1,k=0;
    int tlen=strlen(T);
    while(i<tlen&&j<tlen&&k<tlen)
    {
        int t=T[(i+k)%tlen]-T[(j+k)%tlen];
        if(!t)
            k++;
        else
        {
            if(flag==0)
            {
                if(t>0)
                    i=i+k+1;
                else
                    j=j+k+1;
            }
            else
            {
                if(t>0)
                    j=j+k+1;
                else
                    i=i+k+1;
            }
            if(i==j)
                j++;
            k=0;
        }
    }
    return min(i,j);
}//返回的下标是从0开始的

扩展KMP模板:

即extend里面放的是主串s和模板串t在从主串的第i个位置开始匹配得到的公共前缀长度

void EKMP(char s[],char t[],int Next[],int extend[])//s为主串,t为模板串  
{  
    int i,j,p,L;  
    int lens=strlen(s);  
    int lent=strlen(t);  
    Next[0]=lent;  
    j=0;  
    while(j+1<lent && t[j]==t[j+1])j++;  
    Next[1]=j;  
    int a=1;  
    for(i=2;i<lent;i++)  
    {  
        p=Next[a]+a-1;  
        L=Next[i-a];  
        if(i+L<p+1)Next[i]=L;  
        else  
        {  
            j=max(0,p-i+1);  
            while(i+j<lent&&t[i+j]==t[j])j++;  
            Next[i]=j;  
            a=i;  
        }  
    }  
    j=0;  
    while(j<lens&&j<lent&&s[j]==t[j])j++;  
    extend[0]=j;  
    a=0;  
    for(i=1;i<lens;i++)  
    {  
        p=extend[a]+a-1;  
        L=Next[i-a];  
        if(L+i<p+1)extend[i]=L;  
        else  
        {  
            j=max(0,p-i+1);  
            while(i+j<lens&&j<lent&&s[i+j]==t[j])j++;  
            extend[i]=j;  
            a=i;  
        }  
    }  
} 

Manacher最长回文串算法:

int p[10005];
int str[10005];
int manacher(char s[])//s为要查找的串,str为扩展以后的好串,p为以i为中心的最长回文串长度,注意这个模板是从1开始的,输入的时候要做处理
{
    int i;
    for(i=1;s[i];i++)
        str[i*2]=s[i],str[i*2+1]='#';
    str[0]='?',str[1]='#',str[i*2]='\0';
    int res=0, k=0,maxk=0;
    for(int i = 2; str[i]; i++)
    {
        p[i]=i<maxk?min(maxk-i,p[2*k-i]):1;
        while(str[i-p[i]]==str[i+p[i]])p[i]++;
        if(p[i]+i>maxk)
            k=i,maxk=i+p[i];
        res=max(res,p[i]);
    }
    return res-1;
}
int main()
{
    int i,j;
    char s[10005];
    while(scanf("%s",s+1)!=EOF)
    {
        printf("%d\n",manacher(s));
    }
    return 0;
}


17.输入外挂

可以提高输入速度,有些题用大量的输入卡时间的时候可以用,不同的数据用不同的外挂效率更高

正整数版

void scan_d(int &ret)
{
    char c;
    ret=0;
    while((c=getchar())<'0'||c>'9');
    while(c>='0'&&c<='9')
    {
        ret=ret*10+(c-'0'),c=getchar();
    }
}
正负整数版

bool scan_d(int &ret) 
{
    char c; 
    int sgn;
    if (c = getchar(), c == EOF) 
    {
        return 0; //EOF 
    }
    while (c != '-' && (c < '0' || c > '9')) 
    {
        c = getchar(); 
    }
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0'); 
    while (c = getchar(), c >= '0' && c <= '9') 
    {
        ret = ret * 10 + (c - '0'); 
    }
    ret *= sgn;
    return 1;
}

18.网络流算法

最大流EK算法:邻接表写法模板题传送门:http://blog.csdn.net/qq_37497322/article/details/77825172

                          适用于点较多边较少的稀疏图

                          邻接矩阵模板题传送门:http://blog.csdn.net/qq_37497322/article/details/77834456

                          适用于点较少边很多的稠密图
由于EK算法的复杂度比较高,竞赛的时候一般用EK算法优化的dinic算法,这里给一个模板例题: http://blog.csdn.net/qq_37497322/article/details/77839142


19.java的使用

高精度的题目用java有奇效,这里收集一篇写得好的博客

点击打开链接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值