苹果

Loading

OI模板合集

(AFO) 目录 Contents - V1.1.2


I - 常见加速手段

· 输入输出流


点击查看代码
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);

· 快速读入


点击查看代码
inline int read()
{
    int x = 0,f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
        x = x * 10 + ch - '0',ch = getchar();
    return x * f;
}


II - 基础算法

· 排序


归并排序 Merge Sort
void MergeSort(int l,int r){
	if(l == r){
		return;
	}
	int mid = (l + r) >> 1;
	int x = l,y = mid + 1;
	int now = 1;
	MergeSort(l,mid);
	MergeSort(mid + 1,r);
	while(x <= mid && y <= r){
		b[now++] = a[x] > a[y] ? a[x++] : a[y++];//合并
	}
	while(x <= mid){
		b[now++] = a[x++];
	}
	while(j <= r){
		b[now++] = a[y++];
	}
	for(int i = l;i <= r;i++){
		a[i] = b[i];
	}
}
冒泡排序 Bubble Sort
for(int i = 1;i <= n;i++){
	for(int j = 1;j <= i;j++){
		if(a[j] > a[i]){
			swap(a[i],a[j]);
		}
	}
}


III - 数论

· 辗转相除法


点击查看代码
int gcd(int a,int b){
	if(a > b)swap(a,b);
	return a == 0 ? b : gcd(b % a,a);
}

· 扩展欧几里得定理


点击查看代码
#include <bits/stdc++.h>
using namespace std;
void exgcd(int &x,int &y,int a,int b){
	if(!a){
		x = 0;
		y = 1;
		return;
	}
	exgcd(x,y,b % a,a);
	int temp = y;
	y = x;
	x = temp - (b / a) * x;
}
int main(){
	int a,b,x,y;
	cin >> a >> b;
	exgcd(x,y,a,b);
	cout << x << " " << y;
}

原文讲解

· exgcd的实际应用 - P5656


点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline long long read(){
    long long x = 0,f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
	if(ch == '-'){
            f = -1;
	}
	ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
	x = x * 10 + ch - '0';
	ch = getchar();
    }
    return x * f;
}
void exgcd(int &x,int &y,int a,int b){
    if(!b){
	x = 1;
	y = 0;
	return;
    }
    exgcd(x,y,b,a % b);
    int temp = x;
    x = y;
    y = temp - (a / b) * y;
}
int gcd(int x,int y){
    if(y == 0) return x;
    return gcd(y,x % y);
}
signed main(){
    int t;
    t = read();
    while(t--){
	int a,b,c,x,y;
	a = read();
	b = read();
	c = read();
	if(c % (gcd(a,b)) != 0){
            cout << -1 << endl;
            continue;
	}
	int ttt = gcd(a,b);
	a /= ttt;
	b /= ttt;
	c /= ttt;
	exgcd(x,y,a,b);
	x *= c;
	y *= c;
	int sol;
	if(x <= 0){
            sol = fabs(x) / b + 1;
            x = x % b + b;
	    y -= sol * a;
	    if(y <= 0){
		cout << x << " " << (y % a + a) << endl;
		continue;
    	}
	}
	if(y <= 0){
	    sol = fabs(y) / a + 1;
    	y = y % a + a;
    	x -= sol * b;
    	if(x <= 0){
		cout << (x % b + b) << " " << y << endl;
		continue;
                }
	}
	int x_max,x_min,y_max,y_min;
	sol = x / b;
	x -= sol * b;
	if(x == 0){
            sol--;
            x = b;
        }
	x_min = x;
	y += sol * a;
	y_max = y;
	sol = y / a;
	y -= sol * a;
	if(y == 0){
            sol--;
            y = a;
	}		
	y_min = y;
	x += sol * b;
	x_max = x;
	cout << (x_max - x_min) / b + 1<< " " << x_min;
	cout << " " << y_min << " " << x_max << " " << y_max << endl;
	continue;
    }
}

· 线性筛素数


点击查看代码
//线性筛
#include <bits/stdc++.h>
using namespace std;
int minbeta[5000005];
int pr[5000005];
void primes(int n){
	memset(minbeta,0,sizeof(minbeta));
	int cnt = 0;
	for(int i = 2;i <= n;i++){
		if(!minbeta[i]){
			minbeta[i] = i;
			pr[++cnt] = i;
		}
		for(int j = 1;j <= cnt;j++){
			if(minbeta[i] < pr[j] || i * pr[j] > n)break;
			minbeta[i * pr[j]] = pr[j];
		}
	}
	
}
int main(){
	int n,k;
	cin >> n >> k;
	primes(n);
	while(k--){
		int m;
		cin >> m;
		cout << pr[m] <<endl;
	}
}


IV - 图论

· 链式前向星建图


点击查看代码
const int maxn = 5000005;
int cnt = 0,head[maxn];
struct node{
    int next;
    int to;
    int w;
}e[maxn];
void addedge(int x,int y,int z){
    e[++cnt].to = y;
    e[cnt].next = head[x];
    e[cnt].w = z;
    head[x] = cnt;
}

· Dijkstra (注意不能用于负边权)


点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5000005;
const int inf = 2147483647;
int dots,sides,s;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
//dijkstra
struct node{
    int next;
    int to;
    int w;
}e[maxn];
int cnt_1 = 0,head[maxn];
bool iswhite[maxn];
long long dis[maxn];
void addedge(int x,int y,int z){
    e[++cnt_1].to = y;
    e[cnt_1].next = head[x];
    e[cnt_1].w = z;
    head[x] = cnt_1;
}
void dijkstra(){
    while(!q.empty()){
        int now = q.top().second;
        q.pop();
        if(iswhite[now])continue;
        iswhite[now] = 1;
        for(int i = head[now];i;i = e[i].next){
            if(dis[e[i].to] > dis[now] + e[i].w){
                dis[e[i].to] = dis[now] + e[i].w;
                q.push(make_pair(dis[e[i].to],e[i].to));
            }
        }
    }
    return;
}
int main(){
    ios::sync_with_stdio(false);
    
    int aa,bb,cc;
	cin >> dots >> sides >> s;
    for(int i = 1;i <= dots;i++){
    	dis[i] = 0x3f;
    }
    dis[s] = 0;
    for(int i = 1;i <= sides;i++){
        cin >> aa >> bb >> cc;
        addedge(aa,bb,cc);
        addedge(bb,aa,cc);
    }
    q.push(make_pair(dis[s],s));
    dijkstra();
    for(int i = 1;i <= dots;i++){
        cout << q.top().second << " " << dis[i] << " " << endl;
    }
    return 0;
}

· SPFA (可以处理负边权,但会被卡)


点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5000005;
int n,m,s,ed;
struct node{
    int nxt;
    int to;
    int v;
}edge[maxn];
int cnt = 0,head[maxn];
bool vis[maxn];
long long dis[maxn];
void addedge(int x,int y,int z){
    edge[++cnt].to = y;
    edge[cnt].nxt = head[x];
    edge[cnt].v = z;
    head[x] = cnt;
}
queue<int> q;
int ind[maxn];
bool spfa(int s){
    q.push(s);
    vis[s] = 1;
    dis[s] = 0;
    while(!q.empty()){
        int now = q.front();
        q.pop();
        vis[now] = 0;
        for(int i = head[now];i;i = edge[i].nxt){
            int temp = edge[i].to;
            if(dis[temp] > dis[now] + edge[i].v){
                dis[temp] = dis[now] + edge[i].v;
                if(!vis[temp]){
                    vis[temp] = 1;
                    q.push(temp);
                    if(++ind[temp] > n) return 0;
                }
            }
        }
    }
    return 1;
}
int main(){
    cin >> n >> m >> s >> ed;
    for(int i = 1;i <= n;i++){
        dis[i] = 0x3f;
    }
    dis[s] = 0;
    for(int i = 1;i <= m;i++){
        int xx,yy,zz;
        cin >> xx >> yy >> zz;
        addedge(xx,yy,zz);
        addedge(yy,xx,zz);
    }
    if(!spfa(s)){ 
        cout << "FAIL" << endl;
        return 0;
    }
    cout << dis[ed] << endl;
    return 0;
}

· Floyd


点击查看代码
for(int k = 1;k <= n;k++){
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
        }
    }
}

· Tarjan-scc


Tarjan图解

无向图求桥
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5000005;
int head[MAXN],dfn[MAXN],low[MAXN],n,m;
bool is_br[MAXN];
struct node{
    int to,nxt;
}edge[MAXN];
int cnt = 1;
void addedge(int x,int y){
    edge[++cnt].to = y;
    edge[cnt].nxt = head[x];
    head[x] = cnt;
}
int num = 0;
void tarjan(int root,int trnum){
    dfn[root] = low[root] = ++num;
    for(int i = head[root];i;i = edge[i].nxt){
	int t = edge[i].to;
	if(!dfn[t]){
	    tarjan(t,i);
            low[root] = min(low[root],low[t]);
            if(low[t] > dfn[root]){
	        is_br[i] = 1;
	        is_br[i ^ 1] = 1;
    	    }
	}
	else if(i != (trnum ^ 1)){
    	    low[root] = min(low[root],dfn[t]);
	}
    }
}
int main(){
    cin >> n >> m;
    for(int i = 1;i <= m;i++){
	int x,y;
	cin >> x >> y;
	addedge(x,y);
	addedge(y,x);
    }
    for(int i = 1;i <= n;i++){
	if(!dfn[i]){
    	tarjan(i,0);
	}
    }
    for(int i = 2;i < cnt;i++){
	if(is_br[i]){
    	cout << edge[i].to <<" "<< edge[i ^ 1].to << endl;
	}
    }
}
无向图求割点
int cnt = 0;
void tarjan(int x){
    low[x] = dfn[x] = ++cnt;
    int child = 0;
    for(int i = head[x];i;i = edge[i].to){
	int y = edge[i].to;
	if(!dfn[y]){
    	    fa[y] = x;
    	    child++;
            tarjan(y);
    	    low[x] = min(low[x],low[y]);
    	    if(low[y] >= dfn[x] && fa[x])s.insert(x);
	}
	else if(i != fa[x]){
    	    low[x] = min(low[x],dfn[y]);
        }
    }
    if(!fa[x] && child >= 2)s.insert(x);
}
有向图求强连通分量
stack<int>q;
int cnt = 0;
int sccnum = 0;
int sccno[500005];
void tarjan(int root){
    if(dfs[root]){
	return;
    }
    dfn[root] = low[root] = ++cnt;
    q.push(root);
    for(int i = head[root];i;i = edge[i].nxt){
	if(vis[i]) continue;
	vis[i] = 1;
	vis[i ^ 1] = 1;
	int y = edge[i].to;
	if(!dfn[y]){
            tarjan(y);
            low[root] = min(low[root],low[y]);
        }else{
            low[root] = min(low[root],dfn[y]);
	}
    }
    if(low[root] == dfn[root]){
	sccnum++;
	while(true){
            int temp = q.top();
            q.pop();
            sccno[temp] = sccnum; //sccno数组记录该节点属于第几个强连通分量
            if(temp == root) break;
	}
    }
}


V - 树上问题

· 并查集

路径压缩
int find(int x){
    return fa[x] == x ? x : find(fa[x]);
}
合并
void merge(int x, int y)
{
    fa[find(x)] = find(y);
}
初始化 - 按秩合并
void init(int n)
{
    for (int i = 1; i <= n; i++)
    {
        fa[i] = i;
        rank[i] = 1;
    }
}
按秩合并
inline void merge(int x, int y)
{
    int c = find(x), d = find(y);    //先找到两个根节点
    if(rank[c] <= rank[d]){
        fa[c] = d;
    }  
    else{
        fa[d] = c;
    }     
    if (rank[c] == rank[d] && c != d){
        rank[d]++;    
    }
}

· 二叉树


链式建树
typedef int TreeData;
typedef struct BinaryTree
{
    TreeData x;//数据域
    struct BinaryTree* left;//左子树的根节点
    struct BinaryTree* right;//右子树的根节点
}BTNode;

前序遍历
void PrevOrder(BTNode* root)
{
    if (root == NULL){
        return;
    }
    printf("%c ", root->x);
    PrevOrder(root->left);
    PrevOrder(root->right);
}

中序遍历
void InOrder(BTNode* root)
{
    if (root == NULL){
        return;
    }
    InOrder(root->left);
    printf("%c ", root->x);
    InOrder(root->right);
}

后序遍历
void PostOrder(BTNode* root)
{
    if (root == NULL){
        return;
    }
    PostOrder(root->left);
    PostOrder(root->right);
    printf("%c ", root->x);
}

· 线段树


建树+区间修改+先乘后加+区间询问
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 5000005;
int a[MAXN];
struct node{
    int sum,l,r,plus,muti;
}tr[1000001];
int n,m;
void build(int pos,int l,int r){
    tr[pos].l = l;
    tr[pos].r = r;
    tr[pos].muti = 1;
    if(l == r){
	tr[pos].sum = a[l];
	return;
    }
    int mid = (l + r) >> 1;
    build(pos * 2,l,mid);
    build(pos * 2 + 1,mid + 1,r);
    tr[pos].sum = tr[pos * 2].sum + tr[pos * 2 + 1].sum;
}
void add(int son,int pos){
    tr[son].sum *= tr[pos].muti;
    tr[son].sum += (tr[son].r - tr[son].l + 1) * tr[pos].plus;
    tr[son].muti *= tr[pos].muti;
    tr[son].plus *= tr[pos].muti;
    tr[son].plus += tr[pos].plus;
}
void pushdown(int pos){
    add(pos * 2,pos);
    add(pos * 2 + 1,pos);
    tr[pos].muti = 1;
    tr[pos].plus = 0;
}
void pl(int pos,int l,int r,int k){
    if(tr[pos].l >= l && tr[pos].r <= r){
	tr[pos].plus += k;
	tr[pos].sum += (tr[pos].r - tr[pos].l + 1) * k;
	return;
    }
    pushdown(pos);
    int mid = (tr[pos].l + tr[pos].r) >> 1;
    if(l <= mid){
	pl(pos * 2,l,r,k);
    }
    if(r > mid){
	pl(pos * 2 + 1,l,r,k);
    }
    tr[pos].sum = tr[pos * 2].sum + tr[pos * 2 + 1].sum;
}
void mu(int pos,int l,int r,int k){
    if(tr[pos].l >= l && tr[pos].r <= r){
	tr[pos].muti *= k;
	tr[pos].plus *= k;
	tr[pos].sum *= k;
	return;
    }
    pushdown(pos);
    int mid = (tr[pos].l + tr[pos].r) >> 1;
    if(l <= mid){
	mu(pos * 2,l,r,k);
    }
    if(r > mid){
	mu(pos * 2 + 1,l,r,k);
    }
    tr[pos].sum = tr[pos * 2].sum + tr[pos * 2 + 1].sum;
}
int ask(int pos,int l,int r){
    if(tr[pos].l >= l && tr[pos].r <= r){
	return tr[pos].sum;
    }
    pushdown(pos);
    int ans = 0;
    int mid = (tr[pos].l + tr[pos].r) >> 1;
    if(l <= mid){
	ans += ask(pos * 2,l,r);
    }
    if(r > mid){
	ans += ask(pos * 2 + 1,l,r);
    }
    return ans;
}
signed main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i++){
	cin >> a[i];
    }
    build(1,1,n);
    while(m--){
        int ju;
        cin >> ju;
        if(ju == 1){
            int l,r,k;
            cin >> l >> r >> k;
            mu(1,l,r,k);
        }else if(ju == 2){
            int l,r,k;
            cin >> l >> r >> k;
            pl(1,l,r,k);
        }else if(ju == 3){
            int l,r;
            cin >> l >> r;
            cout << ask(1,l,r) << endl;
        }
    }
}

· 树状数组


五分钟丝滑动画讲解 | 树状数组

树状数组建立+前缀和
int lowbit(x){
    return x & -x;
}
void build(int x,int v){
    for(int i = x;i <= n;i += lowbit(i)){
	c[i] += y;
    }
}
int ask(int x){
    int ans = 0;
    for(int i = x;i;i -= lowbit(i)){
	ans += c[i];
    }
    return ans;
}

· 最小生成树-Kruskal


Kruskal+并查集
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5000005;
int fa[MAXN],m,n;
struct node{
    int from,to,v;
}edge[MAXN];
bool comp(node a,node b){
    return a.v < b.v;
}
void Init(){
    for(int i = 1;i <= n;i++){
	    fa[i] = i;
    }
}
int find(int x){
    return x == fa[x] ? x : find(fa[x]);
}
int cnt = 0,sum = 0;
void kruskal(){
    Init();
    for(int i = 1;i <= m && cnt < n - 1;i++){
		int x = edge[i].from,y = edge[i].to;
		if(find(x) == find(y)) continue;
		fa[find(x)] = find(y);
		cnt++;
		sum += edge[i].v;
    }
} 
int main(){
    cin >> n >> m;
    for(int i = 1;i <= m;i++){
		cin >> edge[i].from >> edge[i].to >> edge[i].v;
    }
    sort(edge + 1,edge + m + 1,comp);
    kruskal();
    if(cnt != n - 1){
		cout << "FAIL" << endl;
    }else{
		cout << sum << endl;
    }
    return 0;
}


VI - 字符串算法

· Hash


Hash子串判断
#include <bits/stdc++.h>
using namespace std;
const int base = 37;
long long hs[500005];
long long hs2;
long long power = 1;
int main(){
    string s,t;
    cin >> s >> t;
    for(int i = 1;i <= s.size();i++){
		hs[i] = hs[i - 1] * base + (s[i - 1] - 'a' + 1);
    }
    for(int i = 1;i <= t.size();i++){
		hs2 = hs2 * base + (t[i - 1] - 'a' + 1);
		power *= base;
    }
    int ans = 0;
    for (int i = 1;i + t.size() - 1 <= s.size();i++) {
        int temp = hs[i + t.size() - 1] - hs[i - 1] * power;
        if (hs2 == temp) {
            ans++;
        }
    }
    if(!ans) cout << "NO" << endl;
    else cout << ans << endl;
}

· KMP


KMP图解

KMP匹配
#include <bits/stdc++.h>
using namespace std;
int nxt[500005];
char b[500005],a[500005];
void get_next(){
    for(int i = 1,j = 0;i <= strlen(b + 1);i++){
    	while(j && b[i + 1] != b[j + 1]) j = nxt[j];
    	if(b[i + 1] == b[j + 1]) j++;
    	nxt[i + 1] = j;
	}
}
void ju(){
    for(int i = 0,j = 0;i <= strlen(a + 1);i++){
		while(j && a[i + 1] != b[j + 1]) j = nxt[j];
		if(a[i + 1] == b[j + 1]) j++;
		if(j == strlen(b + 1)) cout << (i + 2 - strlen(b + 1)) << endl;
    }
}
int main(){
    scanf("%s",a + 1);
    scanf("%s",b + 1);
    get_next();
    ju();
}


VII - 数列相关

· RMQ求区间最大值: 预处理O(logn), 单词查询O(n)


RMQ-区间最大值
#include <bits/stdc++.h>
//rmq 区间最大值
using namespace std;
int n,m;
int a[5005][5005];//a[i][j]:以i为起点走2^j的距离(即走j步)
inline int read()
{
    int x = 0,f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
        x = x * 10 + ch - '0',ch = getchar();
    return x * f;
}
int main(){
    n = read();
    m = read();
    for(int i = 1;i <= n;i++){
		a[i][0] = read();
    }
    for(int j = 1;j <= log2(n);j++){
		for(int i = 1;i + (1 << j) - 1 <= n;i++){
            a[i][j] = max(a[i][j - 1],a[i + (1 << (j - 1))][j - 1]);
		}
	}
	while(m--){
		int x,y;
		x = read();
		y = read();
		int temp = log2(y - x + 1);
		cout << max(a[x][temp],a[y - (1 << temp) + 1][temp]) << endl;//以防因为取整未能取到最大值
    }
}

· LIS - 最长不下降/上升子序列 (O(nlogn)+二分)


使用upper_bound/lower_bound的LIS
#include <bits/stdc++.h>
using namespace std;
int n;
int a[5000005],LIS[5000005],pos[5000005],ans[5000005];
//LCS[i] = j: j记录使最长不下降/上升子序列长度为i的最小值
int main(){
	cin >> n;
    for(int i = 1;i <= n;i++){
    	cin >> a[i];
    }
    LIS[1] = a[1];
    int len = 1;
    pos[1] = 1;
    for (int i = 2;i <= n;i++){
        if (a[i] > LIS[len]){
            LIS[++len] = a[i];
            pos[i] = len;
        }
        else{
            int t = lower_bound(LIS + 1,LIS + 1 + len,a[i]) - LIS;
            //单调二分
            //最长上升子序列  不下降用upper_bound
            LIS[t] = a[i];
            pos[i]=t;
        }
    }
    cout << len << endl;
    int temp = len;
    int cnt = n;
    while (temp){
        while (pos[cnt] != temp) cnt--;
        ans[temp] = a[cnt];
        temp--;
    }
    for(int i = 1;i <= len;i++){
        cout << ans[i] << " ";
    }
    cout << endl;
}

· LCS - 最长公共子序列 (来自于LIS)


使用BinarySearch的LCS
#include <bits/stdc++.h>
using namespace std;
int n,m;
const int MAXN = 5000005;
int a[MAXN],mk[MAXN],c[MAXN],b[MAXN],pos[MAXN],ans[MAXN];
int binary_search(int l,int r,int m){
    if(l == r)return l;
    int mid = (l + r) >> 1;
    if(a[mid] < m){
		return binary_search(mid + 1,r,m);
    }else{
		return binary_search(l,mid,m);
    }
}
int main(){
    cin >> n;
    for(int i = 1;i <= n;i++){
		cin >> a[i];
    }
    int m;
    cin >> m;
    for(int i = 1;i <= m;i++){
		cin >> b[i];
		c[i] = binary_search(1,n,b[i]);
    }
    mk[1] = c[1];
    int len = 1;
    pos[1] = 1;
    for(int i = 2;i <= m;i++){
		if(c[i] > mk[len]){
            mk[++len] = c[i];
            pos[i] = len;
		}
		else{
            int t = lower_bound(mk + 1,mk + 1 + len,c[i]) - mk;
            mk[t] = a[i];
            pos[i] = t;
		}
    }
    cout << len << endl;
    int temp = len;
    int cnt = m;
    while (temp){
        while (pos[cnt] != temp) cnt--;
        ans[temp] = c[cnt];
        temp--;
    }
    for(int i = 1;i <= len;i++){
        cout << a[ans[i]] << " ";
    }
}

Updated on 2022/2/4 V1.1.0
posted @ 2023-02-02 22:38  CatalinaQ  阅读(111)  评论(3)    收藏  举报