(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