1. 程式人生 > 實用技巧 >ACM模板 - 持續更新中

ACM模板 - 持續更新中

快速冪

typedef long long ll;

ll mod_pow(ll x,ll n,ll mod)
{
    ll res=1;
    while(n>0)
    {
        if(n&1)//if(n%2==1)
            res=res*x%mod;
        x=x*x%mod;//把x平方
        n>>=1;//n=n/2 捨去最後一位
    }
    return res;
}

如果用來取模的數本身很大,快速冪會爆掉的話,加上快速乘來優化時間,以達到大數相乘取模

typedef long long ll;
ll mod_pow(ll x,ll n,ll mod)
{
    ll res=1;
    while(n>0)
    {
        if(n&1)
            res = mod_mulit(res,x,mod);
        x = mod_multi(x,x,mod);
        n >>= 1;
    }
    return res;
}

樹狀陣列

查詢區間和,引申:查詢區間最值差

int lowbit(int x)//lowbit(x)表示2^k
{
    return x&(-x);
}

void update(int x,int k)//在位置x增加k
{
    while(x<=n)
    {
        c[x]+=k;
        x+=lowbit(x);
    }
}

int sum(int x)
{
    int res=0;
    while(x>0)
    {
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
}

memset(c,0,sizeof(c));//輸入記得清空

for(int i=1; i<=n; i++)//樹狀陣列處理的是下標為1的陣列
{
    scanf("%d",&a[i]);
    update(i,a[i]);//開始的時候每個元素初始值為0,對應位置加a[i]即可
}

最長公共子序列

char/int a[55],b[55];
int dp[55][55];//dp若是太大,就利用滾動陣列取餘處理
int lcs()
{
    //(char)
    int la=strlen(a);
    int lb=strlen(b);
    for(int i=0;i<la;i++)
    {
        for(int j=0;j<lb;j++)
        {
            if(a[i]==b[j])
                dp[i+1][j+1]=dp[i][j]+1;//兩個陣列整體往後挪一位
            else
                dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
        }
    }
    return dp[la][lb];
}

歐幾里得演算法

(輾轉相除法)

可以呼叫__gcd(a,b); v或者自己寫函式,函式如下

int gcd(int a,int b)
{
    if(b==0)
        return a;
    return(b,a%b);
}

擴充套件歐幾里得

\(ax+by=gcd(a,b)\)的整數解\(x\)\(y\),同時可以求出最大公因數。

int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    int yin=exgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-(a/b)*y;
    //x1=y2,
    //y1=x2-(a/b)*y2;
    return yin;
}

int main()
{
    int a,b,x,y;
    scanf("%d %d",&a,&b);
    int maxx=exgcd(a,b,x,y);//最大公因數
    return 0;
}

擴充套件歐幾里得求逆元

以上兩種解法均要滿足a、p互質。
逆元定義:ax ≡ 1 (mod p) 滿足a乘以x對p取模等於1 ,此時稱 x為a對p的逆元。
只有a與p互質才有逆元 ,互質即gcd(a,p)=1
問法:求 :(a/b)%p;轉化成 (a
x)%p (即需要求b的逆元x),即bx≡1modp,設一個未知數y,則有bx+py=1(x即為所求逆元)(簡介化成擴充套件歐幾里得問題,因為b、p互質,所以gcd(b,p)=1 )。

int inverse(int b,int p)//求b的逆元x
{
    int d=ex_gcd(b,p,x,y);//這裡面的引數x就是所求逆元
    if(d==1)
        return (x%p+p)%p;
    //防止逆元為負,若需要負數作為逆元則return x即可
    return -1;//逆元不存在則返回-1
}

Dijkstra

void  dijkstra()
{
    memset(book,0,sizeof(book));
    for(int i=1;i<=n;i++)
        dis[i]=e[1][i];
    book[1]=1;
    int u;
    for(int i=2;i<=n;i++)
    {
        int minn=inf;
        for(int j=1;j<=n;j++)
        {
            if(book[j]==0&&dis[j]<minn)
            {
                u=j;
                minn=dis[j];
            }
        }
        book[u]=1;
        for(int k=1;k<=n;k++)
        {
            if(e[u][k]<inf&&dis[u]+e[u][k]<dis[k])
            {
                dis[k]=dis[u]+e[u][k];
            }
        }
    }
}

Kruskal

//kruskal
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
#define inff 0x3f3f3f3f3f3f3f3f
const int N=2200;
#define mod 998244353
typedef long long ll;

int f[N];
char a[N][10];

struct node
{
    int l,r,d;
} e[N*N];

int getf(int x)
{
    if(f[x]==x)
        return x;
    return f[x]=getf(f[x]);
}

int merge(int x,int y)
{
    int t1=getf(x);
    int t2=getf(y);
    if(t1!=t2)
    {
        f[t2]=t1;
        return 1;
    }
    return 0;
}

bool cmp1(node x,node y)
{
    return x.d<y.d;
}
int main()
{
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n)
    {
        if(n==0)
            break;
        memset(e,0,sizeof(e));
        /*for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i==j)
                    e[i][j]=0;
                else
                    e[i][j]=inf;
            }
        }*/
        for(int i=1; i<=n; i++)
        {
            scanf("%s",a[i]);
            f[i]=i;
        }
        int p=0;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
                //for(int j=i+1; j<=n; j++)
            {
                int sum=0;
                for(int k=0; k<7; k++)
                {
                    if(a[i][k]!=a[j][k])
                        sum++;
                }
                //e[i][j]=e[j][i]=sum;
                e[p].l=i;
                e[p].r=j;
                e[p++].d=sum;
            }
        }
        int w,ans=0;
        sort(e,e+p,cmp1);
        for(int i=0; i<p; i++)
        {
            if(merge(e[i].l,e[i].r)==1)
            {
                w++;
                ans+=e[i].d;
            }
            if(w==n-1)
                break;
        }
        cout<<"The highest possible quality is 1/"<<ans<<"."<<endl;
    }
    return 0;
}

Prim

//prim
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
using namespace std;
#define inf 0x3f3f3f3f
#define inff 0x3f3f3f3f3f3f3f3f
const int N=2200;
#define mod 998244353
typedef long long ll;

int e[N][N],dist[N];
int n,ans;
bool book[N];
char a[N][10];

void prim()
{
    int countt=1;//countt代表點數,而不是邊數
    ans=0;//記錄路徑長度
    for(int i=1;i<=n;i++)
    {
        dist[i]=e[1][i];
        book[i]=0;
    }
    book[1]=1;
    while(countt<n)
    {
        int minn=inf,u;
        for(int i=1;i<=n;i++)
        {
            if(!book[i]&&dist[i]<minn)
            {
                minn=dist[i];
                u=i;
            }
        }
        ans+=minn;
        countt++;
        book[u]=1;
        for(int i=1;i<=n;i++)
        {
            if(!book[i]&&dist[i]>e[u][i])
            {
                dist[i]=e[u][i];
            }
        }
    }

}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n)
    {
        if(n==0)
            break;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i==j)
                    e[i][j]=0;
                else
                    e[i][j]=inf;
            }
        }
        for(int i=1; i<=n; i++)
            scanf("%s",a[i]);
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
                //for(int j=i+1; j<=n; j++)
            {
                int sum=0;
                for(int k=0; k<7; k++)
                {
                    if(a[i][k]!=a[j][k])
                        sum++;
                }
                e[i][j]=e[j][i]=sum;
            }
        }
        prim();
        cout<<"The highest possible quality is 1/"<<ans<<"."<<endl;
    }
    return 0;
}

最短路和最小生成樹區別

最小生成樹能夠保證整個拓撲圖的所有路徑之和最小,但不能保證任意兩點之間是最短路徑。
最短路徑是從一點出發,到達目的地的路徑最小。

網路流

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
#define inf 0x3f3f3f3f
const int N=220;

int e[N][N],pre[N];
int n,m,s,t;
bool book[N];
int maxflow;


bool bfs()
{
    memset(book,false,sizeof(book));
    memset(pre,0,sizeof(pre));
    queue<int>Q;
    Q.push(s);
    book[s]=true;

   while(!Q.empty())
    {
        int p=Q.front();
        Q.pop();
        if(p==t)
            return true;
        for(int i=1; i<=n; i++)
        {
            if(book[i]==false)
            {
                if(e[p][i]>0)
                {
                    book[i]=true;
                    pre[i]=p;
                    Q.push(i);
                }
            }
        }
    }
    return false;
}


int solve()
{
    maxflow=0;
    while(1)
    {
        if(bfs()==false)
            return maxflow;
        int minn=inf;
        for(int i=t; i!=s; i=pre[i])
            minn=min(minn,e[pre[i]][i]);
        for(int i=t; i!=s; i=pre[i])
        {
            e[pre[i]][i]-=minn;
            e[i][pre[i]]+=minn;
        }
        maxflow+=minn;
    }
}

int main()
{
    int u,v,w;
    int tt=1,ttt;
    {
        while(~scanf("%d %d",&m,&n))
        {
            memset(e,0,sizeof(e));
            s=1,t=n;
            for(int i=1; i<=m; i++)
            {
                scanf("%d %d %d",&u,&v,&w);
                e[u][v]+=w;
            }
            printf("%d\n",solve());
        }
    }
    return 0;
}

KMP

查詢子串(模式串)在原串中出現了幾次。

char s[10020],t[1000020];
int lens,lent;
int nextt[10020];
void getnext()
{
    int i=0,j=-1;
    nextt[0]=-1;
    while(i<lens)
    {
        if(j<0||s[i]==s[j])
        {
            nextt[++i]=++j;
        }
        else
            j=nextt[j];
    }
}

int kmp()
{
    int i=0,j=0,ans=0;
    while(i<lent)
    {
        if(j<0||t[i]==s[j])
        {
            i++;
            j++;
        }
        else
            j=nextt[j];
        if(j==lens)
        {
            ans++;
            j=nextt[j];
        }
    }
    return ans;
}

01揹包

//HDU-4508
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[2000000];
int main()
{
    int w[110],v[110];
    int n,i,m,j;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        for(i=0;i<n;i++)
        {
            scanf("%d %d",&w[i],&v[i]);
        }//w幸福值 價值 v卡路里 重量
        scanf("%d",&m);
        for(i=0;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;
}

SPFA

佇列的寫法:

#include<iostream>
#include<stdio.h>
#include<map>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<string.h>

using namespace std;
#define men(p,b) memset(p,b,sizeof(p))
#define inf 0x3f3f3f3f
typedef long long ll;

const int N=5020;
bool book[N];;
int n,dis1[N],dis2[N],head[N*10],tot;
struct node
{
    int u,v,w,nextt;
}e[260000];

void add(int u,int v,int w)
{
    e[tot].u=u;
    e[tot].v=v;
    e[tot].w=w;
    e[tot].nextt=head[u];
    head[u]=tot++;
}

void SPFA(int x,int*dis)
{
    for(int i=1;i<=n;i++)
    {
//       book[i]=0;
        dis[i]=inf;
    }
    book[x]=1;
    dis[x]=0;
    queue<int> Q;
    Q.push(x);
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        book[u]=0;
        for(int i=head[u];i!=-1;i=e[i].nextt)
        {
            int u=e[i].u,v=e[i].v,w=e[i].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                if(!book[v])
                {
                    book[v]=1;
                    Q.push(v);
                }
            }
        }
    }
}


int main()
{
    int r;
    cin>>n>>r;
    memset(head,-1,sizeof(head));
    tot=0;
    for(int i=1;i<=r;i++)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    SPFA(1,dis1);//1到其他所有點的最短距離
    SPFA(n,dis2);//n到其他所有點的最短距離
    int minn=inf;
    for(int i=1;i<=2*r;i++)
    {
        int u=e[i].u,v=e[i].v,w=e[i].w;
        if(dis1[u]+dis2[v]+w>dis1[n])
            minn=min(minn,dis1[u]+dis2[v]+w);
    }
    cout<<minn<<endl;
    return 0;
}

線段樹

區間修改(A->B)、查詢

#include<iostream>
#include<stdio.h>
#include<map>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<string.h>

using namespace std;
#define mem(p,b) memset(p,b,sizeof(p))
#define inf 0x3f3f3f3f
typedef long long ll;

const int N=1e5+20;
ll a[N<<2],lazy[N<<2];//需要開到節點的四倍大小

void build(int L,int R,int i)
{
    if(L==R)//當左右結點相同的時候,說明該節點可以建樹,輸入即可。
    {
        scanf("%lld",&a[i]);//即為葉子結點
        return;//因為已經確定這個點可以輸入了,也就類似葉結點,返回函式上次呼叫的地方即可。
    }

    //否則往下繼續找
    int mid=(L+R)>>1;
    build(L,mid,i<<1);//遞迴建立左子樹
    build(mid+1,R,i<<1|1);//遞迴建立右子樹
    a[i]=a[i<<1]+a[i<<1|1];//統計該點(i)的左子樹和右子樹之和
    //a這個操作也可以另外寫到一個函式pushup中(即pushup(i)),這個看自己怎麼寫程式碼
    //節點資料向上更新

    //根據題意寫,這一題是求區間和,之前左區間和右區間相加即可
    //例如如果求區間內最大值,則寫成:a[i]=max(a[i<<1],a[i<<1|1]);
}

void pushdown(int i,int len)//節點懶惰標記下推
{
    if(lazy[i])//如果懶惰標記為真,說明之前有過懶惰標記,現在需要進行更新
    {
//        lazy[i<<1]+=lazy[i];//懶惰標記往左結點傳
//        lazy[i<<1|1]+=lazy[i];//懶惰標記往右結點傳
//        //左右用 |1 區分
//        //因為求區間和,所以當區間內每個元素加上一個值時,區間的和也加上這個值
//        //對於區間求和, 原陣列值需要加上lazy標記*子樹所統計的區間長度
//        a[i<<1]+=lazy[i]*(len-(len>>1));//(len-(len>>1)是左區間的長度
//        a[i<<1|1]+=lazy[i]*(len>>1);//(len>>1)是右區間的長度
//        lazy[i]=0;//由於懶惰標記向下傳遞,所以當前節點的懶惰標記取消
        lazy[i<<1]=lazy[i];
        lazy[i<<1|1]=lazy[i];
        a[i<<1]=lazy[i]*(len-(len>>1));
        a[i<<1|1]=lazy[i]*(len>>1);
        lazy[i]=0;
    }
    //對於區間求最大值, 子樹的值不需要乘以長度, 所以不需要傳遞引數區間長度len。
}

//注意:
// 1、單點更新, 不需要用到lazy標記
// 2、成段(區間)更新, 需要用到lazy標記來提高時間效率
void update(int x,int y,int L,int R,int i,int pluss)
{
    if(L>=x&&R<=y)//當前節點區間包含在查詢區間內
        //範圍縮小到left和right之間
    {
//        a[i]+=pluss*(R-L+1);
//        lazy[i]+=pluss;
        a[i]=pluss*(R-L+1);
        lazy[i]=pluss;
        return;
    }
    pushdown(i,R-L+1);
    int mid=(L+R)>>1;

    //更新區間
    if(x<=mid)//更新左區間
        update(x,y,L,mid,i<<1,pluss);
    if(y>mid)//更新右區間
        update(x,y,mid+1,R,i<<1|1,pluss);

    //更新結點值
    a[i]=a[i<<1]+a[i<<1|1];
}

ll query(int x,int y,int L,int R,int i)//查詢操作
{
    if(L>=x&&R<=y)//當前節點區間包含在查詢區間內
        return a[i];//返回當前值
    pushdown(i,R-L+1);
    int mid=(L+R)>>1;
    ll ans=0;
    if(x<=mid)//遞迴查詢左子樹內部的的區間值
        ans+=query(x,y,L,mid,i<<1);
    if(y>mid)//遞迴查詢右子樹內部的的區間值
        ans+=query(x,y,mid+1,R,i<<1|1);
    return ans;//返回題目所需的區間和(左+右)
}

int main()
{
    int n,q;
    scanf("%d",&n);
    build(1,n,1);
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int t,x,y;
        scanf("%d %d %d",&t,&x,&y);
        if(t==0)
            printf("%lld\n",query(x,y,1,n,1));
        else
        {
            int c;
            scanf("%d",&c);
            update(x,y,1,n,1,c);//修改為c,不是加上
        }
    }
    return 0;
}

線段樹區間修改(A->A+B)、查詢

//題意:
//C:對區間[l,r]每一個數+c;
// Q:查詢區間[l,r]的所有元素的總和。

//線段樹修改和查詢的時間複雜度都是O(logn)。
//線段樹基本思想:分治。
//線段樹基本操作:建樹、區間查詢(最值;和)、區間修改(更新)、單點修改、單點查詢。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
#include<map>
#include<stack>
#include<queue>
#include<algorithm>
#include<cmath>

using namespace std;
#define inf 0x3f3f3f3f;
#define pi acos(-1.0)
#define mem(a,b) memset(a,b,sizeof(a))
#define eps 1e-9
typedef long long ll;

const int N=1e5+20;
ll a[N<<2],lazy[N<<2];//需要開到節點的四倍大小

void build(int L,int R,int i)
{
    if(L==R)//當左右結點相同的時候,說明該節點可以建樹,輸入即可。
    {
        scanf("%lld",&a[i]);//即為葉子結點
        return;//因為已經確定這個點可以輸入了,也就類似葉結點,返回函式上次呼叫的地方即可。
    }

    //否則往下繼續找
    int mid=(L+R)>>1;
    build(L,mid,i<<1);//遞迴建立左子樹
    build(mid+1,R,i<<1|1);//遞迴建立右子樹
    a[i]=a[i<<1]+a[i<<1|1];//統計該點(i)的左子樹和右子樹之和
    //a這個操作也可以另外寫到一個函式pushup中(即pushup(i)),這個看自己怎麼寫程式碼
    //節點資料向上更新

    //根據題意寫,這一題是求區間和,之前左區間和右區間相加即可
    //例如如果求區間內最大值,則寫成:a[i]=max(a[i<<1],a[i<<1|1]);
}

void pushdown(int i,int len)//節點懶惰標記下推
{
    if(lazy[i])//如果懶惰標記為真,說明之前有過懶惰標記,現在需要進行更新
    {
        lazy[i<<1]+=lazy[i];//懶惰標記往左結點傳
        lazy[i<<1|1]+=lazy[i];//懶惰標記往右結點傳
        //左右用 |1 區分
        //因為求區間和,所以當區間內每個元素加上一個值時,區間的和也加上這個值
        //對於區間求和, 原陣列值需要加上lazy標記*子樹所統計的區間長度
        a[i<<1]+=lazy[i]*(len-(len>>1));//(len-(len>>1)是左區間的長度
        a[i<<1|1]+=lazy[i]*(len>>1);//(len>>1)是右區間的長度
        lazy[i]=0;//由於懶惰標記向下傳遞,所以當前節點的懶惰標記取消
    }
    //對於區間求最大值, 子樹的值不需要乘以長度, 所以不需要傳遞引數區間長度len。
}

//注意:
// 1、單點更新, 不需要用到lazy標記
// 2、成段(區間)更新, 需要用到lazy標記來提高時間效率
void update(int x,int y,int L,int R,int i,int pluss)
{
    if(L>=x&&R<=y)//當前節點區間包含在查詢區間內
        //範圍縮小到left和right之間
    {
        a[i]+=pluss*(R-L+1);
        lazy[i]+=pluss;
        return;
    }
    pushdown(i,R-L+1);
    int mid=(L+R)>>1;

    //更新區間
    if(x<=mid)//更新左區間
        update(x,y,L,mid,i<<1,pluss);
    if(y>mid)//更新右區間
        update(x,y,mid+1,R,i<<1|1,pluss);

    //更新結點值
    a[i]=a[i<<1]+a[i<<1|1];
}

ll query(int x,int y,int L,int R,int i)//查詢操作
{
    if(L>=x&&R<=y)//當前節點區間包含在查詢區間內
        return a[i];//返回當前值
    pushdown(i,R-L+1);
    int mid=(L+R)>>1;
    ll ans=0;
    if(x<=mid)//遞迴查詢左子樹內部的的區間值
        ans+=query(x,y,L,mid,i<<1);
    if(y>mid)//遞迴查詢右子樹內部的的區間值
        ans+=query(x,y,mid+1,R,i<<1|1);
    return ans;//返回題目所需的區間和(左+右)
}

int main()
{
    int n,q;
    while(~scanf("%d %d",&n,&q))
    {
        mem(lazy,0);//如果多組資料lazy陣列需要進行清空
        mem(a,0);
        build(1,n,1);//開始建樹,傳入樹的總區間(傳入最左端點,最右端點)和樹的根節點
        //建樹的過程中輸入每一個節點
        for(int i=1;i<=q;i++)
        {
            char ch;
            getchar();//吸收每次讀入的空格
            scanf("%c",&ch);
            if(ch=='Q')//詢問區間內的和
            {
                int x,y;
                scanf("%d %d",&x,&y);
                ll ans=query(x,y,1,n,1);
                printf("%lld\n",ans);
            }else if(ch=='C')//往區間內每一個數上都插入pluss
            {
                int x,y,z;
                scanf("%d %d %d",&x,&y,&z);
                update(x,y,1,n,1,z);
            }
        }
    }
    return 0;
}

鄰接表

struct node
{
    int v,flow,nextt;
} e[M];

int tot;

void add(int u,int v,int flow)
{
    e[++tot].nextt=head[u];
    head[u]=tot;
    e[tot].v=v;
    e[tot].flow=flow;

    e[++tot].nextt=head[v];
    head[v]=tot;
    e[tot].v=u;
    e[tot].flow=0;
}