soarli

ACM-template: 算法模板
本文转载自互联网,已在文末注明来源。acm-template 模板编写规范说明使用markdown语法(便于赛前打...
扫描右侧二维码阅读全文
28
2024/11

ACM-template: 算法模板

本文转载自互联网,已在文末注明来源。

acm-template 模板编写规范说明

  • 使用markdown语法(便于赛前打印和生成目录)
  • 所有的算法模板都写在这一个md文件中
  • 代码名称
  • 需要有接口说明(可以看下面示例)
  • 代码最小原则,不添加任何不必要的变量和函数(最小不意味着最少,不要为了减短代码而去减短代码)
  • 代码块要使用如下的markdown包裹
    your code  
  • 其他细则在实践过程中添加

快速幂

简介:介绍算法干啥,哪些情况用的上

非递归版算法

描述: 简要描述算法实现框架(便于找错)。


/*
  Version: 1.0
  Author: 徐祥昊
  Date: 2018.7.27
  Function List:
    1.q_pow(__int64 a,__int64 b)  
      a:这里是a参数的说明;  
      b:这里是b参数的说明;
      ret:这里是返回值的说明;
    2.some else func
    3.some else func
*/

bool q_pow(__int64 a,__int64 b)
{
    __int64 res=1,mod=b,carry=a;
    while(b)
    {
        if(b%2)
        {
            res=res*a%mod;
        }
        a=a*a%mod;
        b>>=1;
    }
    if(res == carry)return true;
    else return false;
}

O(log(n)) 素数判定

目前已知的最快的单个素数判定方法

/*
  Version: 1.0
  Author: 王峰
  Date: 2018.8.26
  Function List:
    isPrime(long long n)
  返回值为n是否是质数
*/
#include <iostream> 
#include <cmath>

long long pow_mod(int a, long long d, long long mod) {
    long long res = 1, k = a;
    while(d) {
        if(d & 1) res = (res * k) % mod; 
        k = k * k % mod;
        d >>= 1;
    } 
    return res;
}
bool test(long long n, int a, long long d) {
    if(n == 2) return true;
    if(n == a) return true;
    if((n & 1) == 0) return false;
    while(!(d & 1)) d = d >> 1;
    long long t = pow_mod(a, d, n);
    while((d != n - 1) && (t != 1) && (t != n - 1)) {
        t = t * t % n;
        d <<= 1; 
    }
    return (t == n - 1 || (d & 1) == 1);
}
bool isPrime(long long n) {
    if(n < 2LL) return false;
    int a[] = {2, 3, 61};
    for (int i = 0; i <= 2; ++i) if(!test(n, a[i], n - 1)) return false;
    return true; 
} 

欧拉筛法求素数表

时间复杂度 O(n)

/*
  Version: 1.0
  Author: 王峰
  Date: 2018.8.26
  Function List:
    get_prime()
  check[i]表示i是否为合数
  prime[i]表示第i个质数,i从0开始计数
*/
#include<cstdio>
#include<cstring>

const int MAXN = 10000001;
const int MAXL = 10000001;
int prime[MAXN];
int check[MAXL];

int get_prime() {
    int tot = 0;
    memset(check, 0, sizeof(check));
    for (int i = 2; i < MAXL; ++i) {
        if (!check[i]){
            prime[tot++] = i;
        }
        for (int j = 0; j < tot; ++j) {
            if (i * prime[j] > MAXL) {
                break;
            }
            check[i*prime[j]] = 1;
            if (i % prime[j] == 0) {
                  break;
            }
        }
    }
    return tot;
}

ST表

用于快速查询RMQ问题 建表时间复杂度O(log(n))空间复杂度O(log(n)) 查询时间复杂度O(1) 不可动态更改

/*
  Version: 1.0
  Author: 王峰
  Date: 2018.8.27
  Function List:
    init_table(n) n为原数组a的长度
    query_table(L, R) 查询原数组内区间[L,R]的最值
*/
#include <cstdio>
#include <algorithm>

using std::min;
const int N = 1000010;

int table[N][100];
int lg[N];
int a[N];

void init_table(int n) {
    for(int i=0; i<n; i++) table[i][0]=a[i];
    for(int j=1; (1<<j)<=n; j++) {
        for(int i=0; i+(1<<j)-1<n; i++) {
            table[i][j] = min(table[i][j-1],table[i+(1<<(j-1))][j-1]);
        }
    } 
    for(int i=2, k=0; i<=n; i++) {
        if(i>(1<<k)) k++;
        lg[i] = k-1;
    }
}

int query_table(int L, int R) {
    int k = lg[R-L+1];
    return min(table[L][k],table[R-(1<<k)+1][k]); 
}

树状数组(二叉索引树)模板

用于求可单点更新的前缀和,时间复杂度O(log(n))

/*
  Version: 1.0
  Author: 王峰
  Date: 2018.8.27
  Function List:
    add(k, v) 将数组中的第k个元素的值+v,注意k从1开始计数
    query(k) 查询profix[k]
*/
#include <cstdio>

const int N = 1000010;
int n; //实际数组的长度 
long long tree[4*N];

void add(int k, long long v) {
    while(k<n){
        tree[k]+=v;
        k+=(k&-k);    
    }
}
long long query(int k) {
    long long ans = 0;
    while(k>0) {
        ans+=tree[k];
        k-=k&-k;
    }
    return ans;
}

求和线段树模板

RMQ线段树小改即可

/*
Function List: 
    Build(1,n,1); 建树 
    Add(L,C,1,n,1); 点更新 
    Update(L,R,C,1,n,1); 区间更新 
    ANS=Query(L,R,1,n,1); 区间查询 
*/

typedef long long LL;

const int MAXN=1e5+7;
LL a[MAXN],ans[MAXN<<2],lazy[MAXN<<2];

void PushUp(int rt) {
    ans[rt]=ans[rt<<1]+ans[rt<<1|1];
}

void Build(int l,int r,int rt) {
    if (l==r) {
        ans[rt]=0;
        return;
    }
    int mid=(l+r)>>1;
    Build(l,mid,rt<<1);
    Build(mid+1,r,rt<<1|1);
    PushUp(rt);
}

void PushDown(int rt,LL ln,LL rn) {
    if (lazy[rt]) {
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        ans[rt<<1]+=lazy[rt]*ln;
        ans[rt<<1|1]+=lazy[rt]*rn;
        lazy[rt]=0;
    }
}

void Add(int L,int C,int l,int r,int rt)
{
    if (l==r)
    {
        ans[rt]+=C;
        return;
    }
    int mid=(l+r)>>1;
    //PushDown(rt,mid-l+1,r-mid); 若既有点更新又有区间更新,需要这句话
    if (L<=mid)
        Add(L,C,l,mid,rt<<1);
    else
        Add(L,C,mid+1,r,rt<<1|1);
    PushUp(rt);
}

void Update(int L,int R,long long C,int l,int r,int rt) {
    if (L<=l&&r<=R) {
        ans[rt]+=C*(r-l+1);
        lazy[rt]+=C;
        return;
    }
    int mid=(l+r)>>1;
    PushDown(rt,mid-l+1,r-mid);
    if (L<=mid) Update(L,R,C,l,mid,rt<<1);
    if (R>mid) Update(L,R,C,mid+1,r,rt<<1|1);
    PushUp(rt);
}

LL Query(int L,int R,int l,int r,int rt) {
    if (L<=l&&r<=R)
        return ans[rt];
    int mid=(l+r)>>1;
    PushDown(rt,mid-l+1,r-mid);
    LL ANS=0;
    if (L<=mid) ANS+=Query(L,R,l,mid,rt<<1);
    if (R>mid) ANS+=Query(L,R,mid+1,r,rt<<1|1);
    return ANS;
}

主席树

主席树:可持久化线段树, 在nlog(n)空内维护多个权值线段树 离线数据结构不支持修改

#include <cstdio>
#include <vector>
#include <algorithm>


/*  
  Version: 1.0
  Author: 王峰
  Date: 2018.9.25
  Function List:
  本例为查询区间第K大,有一个基于排序去重的哈希,有时间分离出来单独成板.
*/

using std::lower_bound;
using std::vector;

const int maxn = 1e5+6;
int cnt, root[maxn], a[maxn];
struct node{
    int l, r, sum;
}T[maxn*40]; 
vector<int > v;

int getid(int x) {
    return lower_bound(v.begin(), v.end(),x)-v.begin()+1;
}

void update(int l, int r, int &x, int y, int pos) {
    T[++cnt]=T[y],T[cnt].sum++,x=cnt;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(mid>=pos)update(l, mid, T[x].l, T[y].l, pos);
    else update(mid+1, r, T[x].r, T[y].r, pos);
}

int query(int l, int r, int x, int y, int k) {
    if(l==r) return l;
    int mid = (l+r)/2;
    int sum = T[T[y].l].sum - T[T[x].l].sum;
    if(sum>=k)return query(l, mid, T[x].l, T[y].l, k);
    else return query(mid+1, r, T[x].r, T[y].r, k-sum);
} 

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for(int i=1; i<=n; i++) {
        scanf("%d", &a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(), v.end()),v.erase(unique(v.begin(), v.end()), v.end());
    for(int i=1; i<=n; i++) update(1, n, root[i], root[i-1], getid(a[i]));
    for(int i=1; i<=q; i++) {
        int x, y, k; 
        scanf("%d%d%d", &x, &y, &k);
        printf("%d\n", v[query(1,n,root[x-1],root[y],k)-1]);
    }
    return 0;
}

dijkstra+priority_queue

用于求单源最短路径,不能处理负环

/*
  Version: 1.0
  Author: 王峰
  Date: 2018.9.3
  Function List:
    init(int n) 将带n个点的图初始化
    addEdge(int from, int to, int dist) 增加一条单向边
    dijkstra(int s) 求s为源点的最短路
    print(t, s) 打印从s为源点t为终点的一条路径 
*/
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>

using std::vector;
using std::priority_queue;
const int maxn = 1e6+7;
const int INF = 0x3f3f3f3f;

struct Edge {
    int from, to, dist;
};

struct HeapNode {
    int d, u;
    bool operator < (const HeapNode& rhs) const {
        return d > rhs.d;
    }
}; 

struct Dijkstra {
    int n, m;                //点数和边数 
    vector<Edge> edges;      //边列表 
    vector<int> G[maxn];     //每个节点出发的边编号, 从0开始编号 
    bool vis[maxn];          //是否已经标记 
    int d[maxn];             //s到各个点的距离 
    //int p[maxn];           //最短路的上一条边 
    
    void init(int n) {
        this->n = n;
        for(int i=0; i<=n; i++) {
            G[i].clear();
        }
        edges.clear();
    }
    
    /*
    void print(int t, int s) {
        int pre = edges[p[t]].from;
        if(t==s) {
            printf("%d ", s);
            return ;
        }else{
            print(pre,s);
            printf("%d ", t);
        }
    }
    */
    
    void addEdge(int from, int to, int dist) {
        edges.push_back((Edge){from, to, dist});
        m = edges.size();
        G[from].push_back(m-1);
    }
    
    void dijkstra(int s) {
        priority_queue<HeapNode> Q;
        for(int i=0; i<=n; i++) d[i] = INF;
        d[s]=0;
        memset(vis, 0, sizeof(vis));
        Q.push((HeapNode){0, s});
        while(!Q.empty()) {
            HeapNode x = Q.top(); Q.pop();
            int u = x.u;
            if(vis[u]) continue;
            vis[u] = true;
            for(int i=0; i<G[u].size(); i++) {
                Edge & e = edges[G[u][i]];
                if(d[e.to] > d[u] + e.dist) {
                    d[e.to] = d[u] + e.dist;
                    //p[e.to] = G[u][i];
                    Q.push((HeapNode){d[e.to], e.to});
                }
            }
        }
    }
}D;

Bellman-Ford

单源最短路-可以判负环 队列实现,时间复杂度O(玄学)

/*
  Version: 1.0
  Author: 王峰
  Date: 2018.9.30
  Function List: the same as dijkstra algorithm
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

using namespace std;

const int maxn = 1e5+7;

struct Edge {
    int from, to;
    double dist;
};

struct BellmanFord {
    int n, m;
    vector<Edge> edges;
    vector<int> G[maxn];
    bool inq[maxn];
    double d[maxn];
    int p[maxn];
    int cnt[maxn];

    void init(int n) {
        this->n = n;
        for(int i=0; i<=n; i++) {
            G[i].clear();
        }
        edges.clear();
    }

    void AddEdge(int from, int to, double dist) {
        edges.push_back((Edge){from, to, dist});
        m = edges.size();
        G[from].push_back(m-1);
    }

    bool negativeCycle() {
        queue<int> Q;
        memset(inq, 0, sizeof(inq));
        memset(cnt, 0, sizeof(cnt));
        for(int i=1; i<=n; i++) {
            d[i]=0;
            inq[0]=true;
            Q.push(i);
        }
        while(!Q.empty()) {
            int u = Q.front();
            Q.pop();
            inq[u] = false;
            for(int i=0; i<G[u].size(); i++) {
                Edge & e = edges[G[u][i]];
                if(d[e.to] > d[u] + e.dist) {
                    d[e.to] = d[u] + e.dist;
                    p[e.to] = G[u][i];
                    if(!inq[e.to]) {
                        Q.push(e.to);
                        inq[e.to] = true;
                        if(++cnt[e.to] > n) return true;
                    }
                }
            }
        }
        return false;
    }
};

hlpp

最大流-最强算法,时间复杂度O(|E|^2*sqrt(|V|))
最高标号预流推进算法High Level Preflow Push

/*
  copy from https://blog.csdn.net/KirinBill/article/details/60882828
  有时间的话参照算法自己实现一遍
*/
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
#define min(a,b) (a<b ? a:b)

const int MAXN=305,MAXM=100005,INF=~0U>>1;
int cnt,head[MAXN];
bool vis[MAXN];

struct line{
    int to,next,cap;
    line(){
        next=-1;
    }
}edge[MAXM];

struct data{
    int x,h,ef;
    friend bool operator< (const data &a,const data &b){
        return a.h<b.h;
    }
}node[MAXN];

priority_queue<data> q;
queue<int> tmpq;

inline void add(int u,int v,int w){
    edge[cnt].next=head[u];
    head[u]=cnt;
    edge[cnt].to=v;
    edge[cnt].cap=w;
    ++cnt;
}

void bfs(int t){
    tmpq.push(t);
    vis[t]=true;
    int u,v;
    while(tmpq.size()){
        u=tmpq.front();
        for(int i=head[u];i!=-1;i=edge[i].next){
            v=edge[i].to;
            if(!vis[v]){
                node[v].h=node[u].h+1;
                tmpq.push(v);
                vis[v]=true;
            }
        }
        tmpq.pop();
    }
}

int hlpp(int n,int s,int t){
    for(int i=1;i<=n;++i)
        node[i].x=i;
    bfs();
    data tmp;
    int u,v,f,tmph;
    for(int i=head[s];i!=-1;i=edge[i].next){
        v=edge[i].to;
        edge[i^1].cap+=edge[i].cap;
        node[v].ef+=edge[i].cap;
        edge[i].cap=0;
        q.push(node[v]);
    }
    node[s].h=n;
    while(q.size()){
        tmp=q.top();
        q.pop();
        u=tmp.x;
        tmph=INF;
        for(int i=head[u];i!=-1 && node[u].ef;i=edge[i].next){
            v=edge[i].to;
            if(edge[i].cap){
                if(node[u].h==node[v].h+1){
                    if(v!=s && v!=t && node[v].ef==0)
                        q.push(node[v]);
                    f=min(edge[i].cap,node[u].ef);
                    edge[i].cap-=f;
                    edge[i^1].cap+=f;
                    node[u].ef-=f;
                    node[v].ef+=f;
                }
                else tmph=min(tmph,node[v].h);
            }
        if(node[u].ef){
            node[u].h=tmph+1;
            q.push(node[u]);
        }
    }
    return node[t].ef;
}

int main(){
    memset(head,-1,sizeof(head));
    int n,m,s,t,tmpu,tmpv,tmpw;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&tmpu,&tmpv,&tmpw);
        add(tmpu,tmpv,tmpw);
        add(tmpv,tmpu,tmpw);
    }
    printf("%d",hlpp(n,s,t));
    return 0;
}

C组合数板子

时间复杂度O(2^n)

/*
  Version: 1.0
  Author: 王峰
  Date: 2018.9.17
*/
C[1][0] = C[1][1] = 1;
for (int i = 2; i < N; i++){
    C[i][0] = 1;
    for (int j = 1; j < N; j++)
        C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]);
}

容斥原理

时间复杂度O(n^2)

ll ans = 0;
for(int i=0; i<(1<<tot); i++) {

    int cnt = 0;
    for(int j=0; j<tot; j++) {
        if(i&(1<<j)) cnt++;
    }

    ll tmp = 0;

        /*
            得到当前状态下的贡献值
        */

    if(cnt&1) {
        ans ++ tmp;
    } else {
        ans -= tmp;
    }
}

原文地址:

GitHub - binjie09/acm-template: 算法模板

最后修改:2024 年 11 月 28 日 12 : 12 AM

发表评论