蓝桥杯赛前模板总结

文章目录

  • 搜索
    • 记忆化搜索
    • IDA*
      • 埃及分数
  • 数论
    • 扩展欧几里得
      • 模板
      • 线性求逆元
    • 欧拉筛法
    • 求欧拉函数
      • 单个
      • 欧拉函数表
    • 整除分块
  • 图论
    • 最短路
      • Dijkstra
      • SPFA
    • 最小生成树
      • Prim
      • Kruskal
    • LCA
      • 倍增法
    • Tarjan
      • 缩点
      • 割点
    • 网络流
      • 最大流Dicnic
      • 最小费用最大流
      • 二分图匹配匈牙利算法
  • 数据结构
    • 线段树
      • 单点查询求区间和
    • 树状数组
      • 单点查询求区间和
      • 区间修改单点查询
      • 区间修改区间查询
  • 字符串
    • Trie树
  • STL
  • 其他
    • 读入优化
    • 对拍
      • 随机数生成器
      • 测试脚本

搜索

记忆化搜索

已知DAG点权,求一条路径使得点权和最大。

void dfs(int x) {
    if(f[x]) return;
    f[x] = a[x];
    int res = 0;
    for(int i=0;i<e[x].size();i++) {
        int v = e[x][i];
        if(!f[v]) dfs(v);
        res = max(res,f[v]);
    }
    f[x] += res;
}

int main() {
	for(int i=1;i<=sum;i++) {
        if(!f[i]) {
            dfs(i);
            ans = max(ans,f[i]);
        }
    }
    return 0;
}

IDA*

埃及分数

ll gcd(ll a,ll b) {
    if(!b) return a;
    return gcd(b,a%b);
}

ll yuefen(ll &a,ll &b) {
    ll d = gcd(a,b);
    a /= d;
    b /= d;
}

ll ans[maxn],a,b,n,now[maxn];
bool flag;

void dfs(ll k,ll pre,ll a,ll b) {
    yuefen(a,b);
    if(k==1) {
        if(a==1 && b>pre) { //由于拆分的分母是单调递增,所以b要比前一个大
            if(flag && b>=ans[1]) return; //如果b不是第一个拆分,那么要求拆分得到的最大的的分母尽量小
            flag = 1;
            now[1] = b;
            memcpy(ans,now,sizeof(ans));
            return;
        }
        return;
    }
    ll f = ceil((double)b*k/a); //当前数为a/b,要求拆分成k个数,每个数最大是1/f
    for(ll i=pre+1;i<=f;i++) {
        now[k] = i;
        dfs(k-1,i,i*a-b,b*i); //1/i作为当前拆分,下一个拆分就用a/b-1/i
    }
}

int main() {
    int ok = 0;
    cin >> a >> b;
    while(!flag) {
        n++;
        dfs(n,1,a,b);
    }
    for(int i=n;i>=1;i--) cout << ans[i] <<' ';
    return 0;
}

数论

扩展欧几里得

模板

int exgcd(int a,int b,int &x,int &y) {
    if(!b) {
        x = 1 , y = 0;
        return a;
    }
    int d = exgcd(b,a%b,y,x);
    y -= x * (a/b);
    return d;
}

线性求逆元

void inverse(int ,int p) {
    inv[1] = 1;
    for(int i=2;i<=n;i++)
        inv[i] = (p-p/i)*inv[p%i]%p;
}

欧拉筛法

const int maxn = 200005;
int tot,prime[maxn],n;
bool vis[maxn];

void euler(int n) {
    for(int i=2;i<=n;i++) {
        if(!vis[i]) prime[tot++] = i;
        for(int j=0;prime[j]*i<=n;j++) {
            vis[i*prime[j]] = 1;
            if(i%prime[j]==0) break;
        }
    }
}

求欧拉函数

单个

ll phi(ll n) {
    ll res = n;
    for(int i=2;i*i<=n;i++) {
        if(n%i==0) {
            res = res / i * (i-1);
            while(n%i==0) n /= i;
        }
    }
    if(n>1) res = res / n * (n-1);
    return res;
}

欧拉函数表

void euler(ll n) {
    for(int i=2;i<=n;i++) {
        if(!vis[i]) {
            prime[tot++] = i;
            phi[i] = i-1;
        }
        for(int j=0;i*prime[j]<=n;j++) {
            vis[i*prime[j]] = 1;
            if (i%prime[j]) {
                phi[i*prime[j]] = phi[i] * (prime[j] - 1);
            } else {
                phi[i*prime[j]] = phi[i] * prime[j];
                break;
            }
        }
    }
}

整除分块

∑ i = 1 n ⌊ n i ⌋ , n ≤ 1 0 12 \sum_{i=1}^{n} \lfloor \frac{n}{i}\rfloor, n\leq 10^{12} i=1nin,n1012

for(int l=1;l<=n;l=r+1) {
	r = n / (n / l);
	ans += (r-l+1) * (n/l);
}

图论

最短路

Dijkstra

void dijkstra(int s) {
    for(int i=1;i<=n;i++) d[i] = 2147483647;
    d[s] = 0;
    priority_queue<pair<int,int> >q;
    q.push(make_pair(-d[s],s));
    while(!q.empty()) {
        int u = q.top().second; q.pop();
        for(int i=0;i<e[u].size();i++) {
            int v = e[u][i].first;
            if(d[v] > d[u] + e[u][i].second) {
                d[v] = d[u] + e[u][i].second;
                q.push(make_pair(-d[v],v));
            }
        }
    }
}

SPFA

void SPFA(int s) {
    for(int i=1;i<=n;i++) d[i] = 2147483647;
    d[s] = 0;
    deque<int> q;
    q.push_front(s);
    while(!q.empty()) {
        int u = q.front(); q.pop_front();
        inq[u] = 0;
        for(int i=0;i<e[u].size();i++) {
            int v = e[u][i].first;
            if(d[v] > d[u] + e[u][i].second) {
                d[v] = d[u] + e[u][i].second;
                if(inq[v]) continue;
                inq[v]++;
                if(d[v]<d[u]) q.push_front(v);
                else q.push_back(v);
            }
        }
    }
}

最小生成树

Prim

int prim() {
	int res = 0, cnt = 0;
	memset(dis,0x3f,sizeof(dis));
	dis[1] = 0;
	while(1) {
		int u = 0, mini=inf;
		for(int i=1;i<=n;i++) {
			if(mini>dis[i] && !vis[i]) {
				mini = dis[i];
				u = i; 
			}
		}
		if(!u) break;
		vis[u] = 1;
		cnt++;
		res += mini;
		for(int i=0;i<e[u].size();i++) {
			int v = e[u][i].first, w = e[u][i].second;
			dis[v] = min(dis[v],w);
		} 
	}	
	if(cnt!=n) return -1;
	return res;
}

Kruskal

#include 
using namespace std;

const int maxn = 200005;

int n,m,s,cnt=1,fa[maxn],d[maxn],ans;
struct node{
    int x,y,z;
    bool operator<(const node &b)const {
        return z < b.z;
    }
}e[maxn];

int get(int x) {
    if(fa[x]==x) return x;
    return fa[x]=get(fa[x]);
}

int main() {
    read(n), read(m);
    for(int i=1;i<=n;i++) fa[i] = i;
    for(int i=0;i<m;i++) {
        read(e[i].x), read(e[i].y), read(e[i].z);
    }
    sort(e,e+m);
    for(int i=0,a,b,x,y;i<m;i++) {
        a = e[i].x;
        b = e[i].y;
        x = get(a);
        y = get(b);
        if(x==y) continue;
        fa[x] = y;
        ans += e[i].z;
        cnt++;
    }    
    if(cnt<n) cout << "orz" << endl;
    else cout << ans << endl;
    return 0;
}

LCA

倍增法

注意需要预处理lg数组,即使用init函数

ll lg[maxn],anc[maxn][50],dep[maxn];
vector<int> e[maxn];
int n,m,s;

void init(int n) {
    for(int i=1;i<=n;i++)
        lg[i] = lg[i-1] + (1<<(lg[i-1]+1)==i);
}

void dfs(int x,int pre,int depth) {
    dep[x] = depth;
    anc[x][0] = pre;
    for(int i=1;(1<<i)<=depth;i++)
        anc[x][i] = anc[anc[x][i-1]][i-1];
    for(auto i:e[x]) {
        if(i!=pre) dfs(i,x,depth+1);
    }
}

int lca(int x,int y) {
    if(dep[x]<dep[y]) swap(x,y);
    while(dep[x]>dep[y]) {
        int d = dep[x] - dep[y];
        x = anc[x][lg[d]];
    }
    if(x==y) return x;
    for(int i=lg[dep[x]];i>=0;i--) {
        if(anc[x][i]!=anc[y][i]) {
            x = anc[x][i], y = anc[y][i];
        }
    }
    return anc[x][0];
}

Tarjan

缩点

将环缩成一个点。

void tarjan(int u) {
    dfn[u] = low[u] = ++cnt;
    s[++st] = u;
    vis[u] = 1;
    for(int i=0;i<e[u].size();i++) {
        int v = e[u][i];
        if(!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u],low[v]);
        } else if(vis[v]) low[u] = min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]) {
        sum++;
        while(s[st]!=u) {
            int f = s[st];
            a[sum] += b[f]; //点权值累加给新点
            color[f] = sum;
            vis[f] = 0;
            st--;
        }
        st--;
        color[u] = sum;
        a[sum] += b[u]; 
        vis[u] = 0;
    }
}

割点

去掉当前点及其所连的边,若原图不再联通,则称为割点。

  1. 若点为根节点,则它至少有两棵子树;
  2. 若点为非根节点,则满足 d f n [ v ] ≤ l o w [ u ] dfn[v] \leq low[u] dfn[v]low[u],即子树和祖先节点不连通。
void tarjan(int u,int fa) {
    int child = 0;
    dfn[u] = low[u] = ++cnt;
    for(int i=0;i<e[u].size();i++) {
        int v = e[u][i];
        if(!dfn[v]) {
            tarjan(v,fa);
            low[u] = min(low[u],low[v]);
            if(low[v]>=dfn[u] && u!=fa) mp[u]=1;
            if(u==fa) child++;
        } else if(u!=fa) low[u] = min(low[u],dfn[v]);
    }
    if(u==fa && child>=2) mp[u] = 1;
}

网络流

最大流Dicnic

// luogu-judger-enable-o2
#include 
using namespace std;
const int maxn = 200005;
const int inf = 0x3f3f3f3f;

void read(int &x) {
    int f=1;x=0;char s = getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x *= f;
}

int dep[maxn],head[maxn],n,m,s,t;
int tot = 1;
bool inq[maxn];

struct node{
    int v,w,next;
}e[maxn];

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

bool bfs(int s,int t) {
    memset(dep,inf,sizeof(dep));
    memset(inq,0,sizeof(inq));
    inq[s] = 1, dep[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        inq[u] = 0;
        for(int i=head[u];i;i=e[i].next) {
            int v = e[i].v;
            if(dep[v]>dep[u]+1 && e[i].w) {
                dep[v] = dep[u] + 1;
                if(!inq[v]) {
                    inq[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    if(dep[t]==inf) return 0;
    return 1;
}

int dfs(int u,int flow) {
    if(u==t) return flow;
    for(int i=head[u];i;i=e[i].next) {
        int v = e[i].v;
        if(dep[v]==dep[u]+1 && e[i].w) {
            int f = dfs(v,min(flow,e[i].w));
            if(f) {
                e[i].w -= f;
                e[i^1].w += f;
                return f;
            }
        }
    }
    return 0;
}

int Dicnic(int s,int t) {
    int res = 0;
    while(bfs(s,t)) {
        int f = dfs(s,inf); 
        while(f) {
            res += f;
            f = dfs(s,inf);
        }
    }
    return res;
}

int main() {
    read(n), read(m), read(s), read(t);
    for(int i=0,u,v,w;i<m;i++) {
        read(u), read(v), read(w);
        addedge(u,v,w);
        addedge(v,u,0);
    }
    cout << Dicnic(s,t) << endl;
    return 0;
}

最小费用最大流

#include 
using namespace std;
const int inf=0x3f3f3f3f;
int top=1,head[5010];
int n,m,s,t;
int cost,maxflow;
int vis[5010];//是否到达过该点
int dist[5010];//到t的单位费用
struct Node {
    int u,v,val,w,next;
} node[101010];

void read(int &x) {
    int f=1;x=0;char s = getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x *= f;
}

inline void addedge(int u,int v,int val,int w) {
    node[++top].v=v;
    node[top].w=w;
    node[top].val=val;
    node[top].next=head[u];
    head[u]=top;
}
bool spfa() {
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    dist[s]=0;
    vis[s]=1;
    queue<int>q;
    q.push(s);
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u]; i; i=node[i].next) {
            int d=node[i].v;
            if(dist[d]>dist[u]+node[i].w&&node[i].val) {
                dist[d]=dist[u]+node[i].w;
                if(vis[d]==0) {
                    q.push(d);
                    vis[d]=1;
                }
            }
        }
    }
    return dist[t]!=0x3f3f3f3f;
}//spfa同EK

int dfs(int u,int flow) {
    if(u==t) {
        vis[t]=1;    //可以到达t,加流
        maxflow+=flow;
        return flow;
    }
    int used=0;//该条路径可用流量
    vis[u]=1;
    for(int i=head[u]; i; i=node[i].next) {
        int d=node[i].v;
        if((vis[d]==0||d==t)&&node[i].val!=0&&dist[d]==dist[u]+node[i].w) {
            int minflow=dfs(d,min(flow-used,node[i].val));
            if(minflow!=0)cost+=node[i].w*minflow,node[i].val-=minflow,node[i^1].val+=minflow,used+=minflow;
            //可以到达t,加费用,扣流量
            if(used==flow)break;
        }
    }
    return used;
}
int mincostmaxflow() {
    while(spfa()) {
        vis[t]=1;
        while(vis[t]) {
            memset(vis,0,sizeof(vis));
            dfs(s,inf);
        }
    }
    return maxflow;
}

int main() {
    read(n), read(m), read(s), read(t);
    for(int i=1,u,v,w,val; i<=m; i++) {
        read(u), read(v), read(val), read(w);
        addedge(u,v,val,w);
        addedge(v,u,0,-w);
    }
    mincostmaxflow();
    printf("%d %d",maxflow,cost);
    return 0;
}

二分图匹配匈牙利算法

#include 
using namespace std;

const int maxn = 1005;
void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x *= f;
}

int n,m,e,ans;
int a[maxn][maxn],g[maxn];
bool vis[maxn];

int find(int x) {
    for(int i=1;i<=m;i++) {
        if(a[x][i] && !vis[i]) {
            vis[i] = 1;
            if(!g[i] || find(g[i])) {
                g[i] = x;
                return 1;
            }
        }
    }
    return 0;
}

int main() {
    read(n), read(m), read(e);
    for(int i=0,x,y;i<e;i++) {
        read(x), read(y);
        if(x>n || y>m) continue;
        a[x][y] = 1;
    }
    for(int i=1;i<=n;i++) {
        memset(vis,0,sizeof(vis));
        ans += find(i);
    }
    cout << ans << endl;
    return 0;
}

数据结构

线段树

单点查询求区间和

struct node{
    int l,r,sum;
}t[maxn*4];

int a[maxn],n,m;

void build(int x,int l,int r) {
    t[x].l = l, t[x].r = r;
    if(l==r) {t[x].sum=a[l];return;}
    int mid = (l+r) / 2;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
}

void add(int x,int p,int y) {
    int l = t[x].l, r = t[x].r;
    if(l==r) {
        t[x].sum += y;
        return;
    } 
    int mid = (l+r) / 2;
    if(p<=mid) add(x<<1,p,y);
    if(p>mid) add(x<<1|1,p,y);
    t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
}

int query(int x,int L,int R) {
    int l = t[x].l, r = t[x].r, res=0;
    if(L<=l && R>=r) {
        return t[x].sum;
    }
    int mid = (l+r) / 2;
    if(L<=mid) res += query(x<<1,L,R);
    if(R>mid) res += query(x<<1|1,L,R);
    return res;
}

int main() {
    read(n), read(m);
    for(int i=1;i<=n;i++) read(a[i]);
    build(1,1,n);
    for(int i=0,x,y,flag;i<m;i++) {
        read(flag), read(x), read(y);
        if(flag==1) {
            add(1,x,y);
        } else {
            cout << query(1,x,y) << endl;
        }
    }
    return 0;
}

树状数组

单点查询求区间和

inline int lowbit(int x) {return x&(-x);}

void add(int x,int y) {
    for(;x<=n;x+=lowbit(x)) {
        t[x] += y;
    }
}

int query(int x) {
    int res = 0;
    for(;x;x-=lowbit(x)) res += t[x];
    return res;
}

区间修改单点查询

#include 
#include 
#include 

using namespace std;
const int maxn = 500005;

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x *= f;
}

int n,m,a[maxn],t[maxn];

inline int lowbit(int x) {return x&(-x);}

void add(int x,int y) {
    for(;x<=n;x+=lowbit(x)) {
        t[x] += y;
    }
}

int query(int x) {
    int res = 0;
    for(;x;x-=lowbit(x)) res += t[x];
    return res;
}

int main() {
    read(n), read(m);
    for(int i=1;i<=n;i++) {read(a[i]);}
    for(int i=0,x,y,z,flag;i<m;i++) {
        read(flag);
        if(flag==1) {
            read(x), read(y), read(z);
            add(x,z);
            add(y+1,-z);
        } else {
            read(x);
            cout << query(x)+a[x] << endl;
        }
    }
    return 0;
}

区间修改区间查询

#include 
#include 
#include 

using namespace std;
const int maxn = 500005;
typedef long long ll;

inline void read(ll &x) {
    ll f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x *= f;
}

ll c[2][maxn],a[maxn],n,m,sum[maxn];

inline ll lowbit(ll x) {return x&-x;}

void add(ll k,ll x,ll y) {
    for(;x<=n;x+=lowbit(x)) c[k][x] += y;
}

ll query(ll k,ll x) {
    ll res = 0;
    for(;x;x-=lowbit(x)) res += c[k][x];
    return res;
}

ll getsum(ll x) {
    return sum[x]+query(0,x)*(x+1)-query(1,x);
}

int main() {
    read(n), read(m);
    for(int i=1;i<=n;i++) {
        read(a[i]);
        sum[i] = sum[i-1] + a[i];
    }
    for(int i=0;i<m;i++) {
        ll flag,x,y,z;
        read(flag);
        if(flag==1) {
            read(x), read(y), read(z);
            add(0,x,z);
            add(0,y+1,-z);
            add(1,x,z*x);
            add(1,y+1,-(y+1)*z);
        } else {
            read(x), read(y);
            ll ans = getsum(y) - getsum(x-1);
            cout << ans << endl;
        }
    }
    return 0;
}

字符串

Trie树

给定n个字符串,再给出m个询问,每个询问有一个字符串s,问s是否是之前字符串的前缀。

const int maxn = 1000005; //建立trie树时空间要开大 
int trie[maxn][27];

void insert(string s) {
	int n = s.length();
	int p = 0;
	for(int i=0;i<n;i++) {
		int c = s[i]-'a';
		if(!trie[p][c]) {
			trie[p][c] = ++cnt;
		}
		p = trie[p][c];
		ans[p]++;
	}	
}

int search(string s) {
	int res = 0, p = 0;
	int n = s.length();
	for(int i=0;i<n;i++) {
		int c = s[i] - 'a';
		p = trie[p][c];
		if(!p) return 0;
	}
	return ans[p];
}

STL

set<int>::iterator it=s.begin()

其他

读入优化

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

对拍

随机数生成器

#include 
srand( (unsigned)time( NULL ) );
n = (rand() << 16) + rand();
n = n%10 + 1; //产生[1,10]的随机数 

测试脚本

:loop
generate.exe > data.txt   
main.exe < data.txt > std.txt  
duipai.exe < data.txt > ans.txt 
fc /A std.txt ans.txt
if not errorlevel 1 goto loop
pause
:end

你可能感兴趣的:(算法竞赛)