快速输入输出(IO)
cin/cout 加速
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int 快读
inline int read() {
int x = 0, f = 1; // f 标记正负(1 正 / -1 负)
char ch = getchar();
while (ch < '0' || ch > '9') { // 过滤乱码及确定正负
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48); // 前两项为(2+8)x=10x,第三项相当于ch-'0'
ch = getchar();
}
return f * x;
}
inline void Read(int &t) {
t = read();
}
int 快写
inline void write(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
inline void Write(int x) {
write(x);
putchar('\n');
}
基础算法
前缀和
for (int i = 1; i <= n; i ++) {
cin >> arr[i];
sum[i] = sum[i - 1] + arr[i];
}
差分
for (int i = 1; i <= n; i ++) {
cin >> arr[i];
dif[i] = arr[i] - arr[i - 1];
}
二分答案
int l = 0, r = 1e9, ans = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
二分查找
int binary_search(int target) {
int l = 1, r = n, mid = -1;
while (l <= r) {
mid = (l + r) >> 1;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) l = mid + 1;
else r = mid - 1;
}
return mid;
}
可分割背包(贪心)
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
ll n, t;
struct Node {
ll m, v; // 重量、价值
double a; // 单位价格
} a[105];
bool cmp(Node ta, Node tb) {
return ta.a > tb.a;
}
int main() {
cin >> n >> t;
for (int i = 1; i <= n; i ++) {
cin >> a[i].m >> a[i].v;
a[i].a = 1.0 * a[i].v / a[i].m;
}
sort(a + 1, a + n + 1, cmp);
ll left = t;
double ans = 0.0;
for (int i = 1; i <= n; i ++) {
ll am = min(a[i].m, left);
left -= am;
ans += am * a[i].a;
if (left == 0) break;
}
printf("%.2lf\n", ans);
return 0;
}
搜索枚举
dfs 枚举排列
int n, k;
int per[n];
bool vis[n];
void dfs(int dep) {
if (dep == k) {
for (int i = 0; i < k; i ++) {
cout << per[i] << " ";
}
cout << endl;
return;
}
for (int i = 1; i <= n; i ++) {
if (vis[i]) continue;
vis[i] = true;
per[dep] = i;
dfs(dep + 1);
vis[i] = false;
}
}
dfs 枚举组合
int n, k;
int comb[N];
void dfs(int x, int dep) {
if (dep == k) {
for (int i = 0; i < k; i ++) {
cout << comb[i] << " ";
}
cout << endl;
return;
}
if (x > n) return;
comb[dep] = x;
dfs(x + 1, dep + 1);
dfs(x + 1, dep);
}
bfs 走迷宫
typedef pair<int, int> PII;
int n, m;
int arr[105][105]; // 0 -> 空地;1 -> 障碍
bool vis[105][105];
int cx[] = {0, 0, 1, -1};
int cy[] = {1, -1, 0, 0};
bool found = false;
void bfs() {
queue<PII> q;
q.push({1, 1});
while (!q.empty()) {
PII cur = q.front();
q.pop();
int x = cur.first, y = cur.second;
if (x < 1 || x > n || y < 1 || y > m) continue;
if (arr[x][y] == 1) continue;
if (vis[x][y]) continue;
vis[x][y] = true;
if (x == n && y == m) {
found = true;
return;
}
for (int i = 0; i < 4; i ++) {
q.push({x + cx[i], y + cy[i]});
}
}
}
动态规划
01 背包
$w$ 表示重量,$v$ 表示价值,$N$ 表示物品个数,$W$ 表示背包容量,$f$ 表示 $dp$ 数组
for (int i = 1; i <= N; i ++) {
for (int j = W; j >= w[i]; j --)
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
完全背包
$w$ 表示重量,$v$ 表示价值,$N$ 表示物品个数,$W$ 表示背包容量,$f$ 表示 $dp$ 数组
for (int i = 1; i <= N; i ++) {
for (int j = w[i]; j <= W; j ++)
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
二维费用背包
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll H, T, n; // 体积最大值,质量最大值,物品个数
ll dp[405][405]; // 体积为 i,质量为 j 所能承载的最大价值
ll h[55]; // 体积
ll t[55]; // 质量
ll k[55]; // 价值
int main() {
cin >> H >> T >> n;
for (int i = 1; i <= n; i ++) {
cin >> h[i] >> t[i] >> k[i];
}
for (int l = 1; l <= n; l ++) {
for (int i = H; i >= h[l]; i --) {
for (int j = T; j >= t[l]; j --) {
dp[i][j] = max(dp[i][j], dp[i - h[l]][j - t[l]] + k[l]);
}
}
}
cout << dp[H][T] << endl;
return 0;
}
分组背包
#include <iostream>
#include <vector>
using namespace std;
int dp[1010], n[21];
vector<int> w[21], c[21];
int main() {
int K, V;
cin >> K >> V;
for (int i = 1; i <= K; i ++) {
cin >> n[i];
for (int j = 0; j < n[i]; j ++) {
int worth, cost;
cin >> worth >> cost;
w[i].push_back(worth);
c[i].push_back(cost);
}
}
for (int i = 1; i <= K; i ++) {
for (int j = V; j >= 0; j --) {
for (int k = 0; k < n[i]; k ++) {
if (j >= c[i][k]) {
dp[j] = max(dp[j], dp[j - c[i][k]] + w[i][k]);
}
}
}
}
cout << dp[V] << endl;
return 0;
}
多重背包(无二进制优化)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[105];
ll weight[10004], value[10004], sum = 0;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll N, V;
cin >> N >> V;
for (ll i = 1; i <= N; i++) {
ll tweight, tvalue, s;
cin >> tweight >> tvalue >> s;
for (ll j = 1; j <= s; j ++) {
sum ++;
weight[sum] = tweight;
value[sum] = tvalue;
}
}
for (ll i = 1; i <= sum; i++) {
for (ll j = V; j >= weight[i]; j --) {
dp[j] = max(dp[j - weight[i]] + value[i], dp[j]);
}
}
cout << dp[V] << endl;
return 0;
}
多重背包(有二进制优化)
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[20005];
ll weight[11000], value[11000], sum = 0;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll N, V;
cin >> N >> V;
for (ll i = 1; i <= N; i++) {
ll tweight, tvalue, s;
cin >> tweight >> tvalue >> s;
if (s == 0) s = 1000;
ll rate = 1;
while (rate <= s) {
sum ++;
weight[sum] = tweight * rate;
value[sum] = tvalue * rate;
s -= rate;
rate *= 2;
}
if (s > 0) {
sum ++;
weight[sum] = tweight * s;
value[sum] = tvalue * s;
}
}
for (ll i = 1; i <= sum; i ++) {
for (ll j = V; j >= weight[i]; j --) {
dp[j] = max(dp[j - weight[i]] + value[i], dp[j]);
}
}
cout << dp[V] << endl;
return 0;
}
分组背包
#include <bits/stdc++.h>
using namespace std;
typedef long long lint;
lint n, m, grocnt = -1;
lint wei[1005];
lint val[1005];
lint group[1005];
lint dp[1005];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> m >> n;
for (int i = 1; i <= n; i ++) {
cin >> wei[i] >> val[i] >> group[i];
grocnt = max(grocnt, group[i]);
}
for (int i = 1; i <= grocnt; i ++) {
for (int j = m; j >= 0; j --) {
for (int k = 1; k <= n; k ++) {
if (group[k] != i || j < wei[k]) continue;
dp[j] = max(dp[j], dp[j - wei[k]] + val[k]);
}
}
}
cout << dp[m] << endl;
return 0;
}
混合背包问题
// 混合背包问题(AcWing 7)
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[1005];
ll weight[110004], value[110004], sum = 0;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll N, V;
cin >> N >> V;
for (ll i = 1; i <= N; i++) {
ll tweight, tvalue, s;
cin >> tweight >> tvalue >> s;
if (s == 0) s = 1000;
if (s == -1) s = 1;
ll rate = 1;
while (rate <= s) {
sum ++;
weight[sum] = tweight * rate;
value[sum] = tvalue * rate;
s -= rate;
rate *= 2;
}
if (s > 0) {
sum ++;
weight[sum] = tweight * s;
value[sum] = tvalue * s;
}
}
for (ll i = 1; i <= sum; i ++) {
for (ll j = V; j >= weight[i]; j --) {
dp[j] = max(dp[j - weight[i]] + value[i], dp[j]);
}
}
cout << dp[V] << endl;
return 0;
}
最大子段和
cin >> n;
for (int i = 1; i <= n; i ++) {
ll x;
cin >> x;
f[i] = max(f[i-1] + x, x);
}
ll ans = -1e18;
for (int i = 1; i <= n; i ++) {
ans = max(ans, f[i]);
}
cout << ans << endl;
最长上升子序列(二分优化)
#include <bits/stdc++.h>
using namespace std;
int n = 0;
int a[1000006] = {};
int g[1000006] = {};
int len = 0;
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
for (int i = 1; i <= n; i ++) {
int pos = lower_bound(g + 1, g + len + 1, a[i]) - g;
g[pos] = a[i];
len = max(len, pos);
}
cout << len << endl;
return 0;
}
最长不降子序列(二分优化)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n = 0;
ll arr[500005];
ll dp[500005];
ll my_lwb(ll le, ll x) {
ll l = 1, r = le;
while (l <= r) {
ll mid = (l + r) >> 1;
if (dp[mid] >= x) l = mid + 1;
else r = mid - 1;
}
return l;
}
int main() {
while (cin >> arr[++n]); n --;
ll len = 0;
for (ll i = 1; i <= n; i ++) {
ll pos = my_lwb(len, arr[i]);
dp[pos] = arr[i];
len = max(len, pos);
}
cout << len << endl;
return 0;
}
动态规划求回文子串
int f[2020][2020];
string s = "cicabadbd";
void huiwen() {
int n = s.length();
for (int i = 0; i < n; i ++) {
f[i][i] = 1;
if (i < n - 1 && s[i] == s[i + 1]) {
f[i][i + 1] = 1;
}
}
for (int l = 3; l <= n; l ++) { // 区间长度
for (int i = 0; i + l - 1 < n; i ++) { // 区间左端点
int j = i + l - 1; // 区间右端点
if (s[i] == s[j] && f[i + 1][j - 1]) {
f[i][j] = 1;
}
}
}
}
破环成链
for (lint i = 1; i <= n; i ++) {
arr[i + n] = arr[i];
}
lint ans = -1;
for (int i = 2; i < 2 * n; i ++) {
for (int j = i - 1; i - j < n && j >= 1; j --) {
for (int k = j; k < i; k ++) {
ans = max(ans, dp[j][i]);
}
}
}
cout << ans << endl;
区间动态规划(小区间至大区间)
for (lint len = 2; len <= n; len ++) {
for (lint i = 1; i <= n - len + 1; i ++) {
lint j = i + len - 1;
}
}
区间动态规划(大区间至小区间)
for (int l = 1; l <= n; l ++) {
for (int r = n; r >= l; r --) {
dp[l][r] = 0;
}
}
最长公共子序列(无二分优化)
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int dp[501][501];
int a[501], b[501];
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) cin >> b[i];
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) {
if (a[i] == b[j]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
}
}
cout << dp[n][m] << endl;
return 0;
}
最长公共子序列(有二分优化)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N], b[N], arr[N], f[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int n = 0;
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
arr[a[i]] = i;
}
for (int i = 1; i <= n; i ++) {
cin >> b[i];
f[i] = INT_MAX;
}
int len = 0;
f[0] = 0;
for (int i = 1; i <= n; i ++) {
int l = 0, r = len, mid = -1;
if (arr[b[i]] > f[len]) {
len ++;
f[len] = arr[b[i]];
} else {
while (l <= r) {
mid = (l + r) >> 1;
if (f[mid] == arr[b[i]]) break;
else if (f[mid] > arr[b[i]]) r = mid - 1;
else if (f[mid] < arr[b[i]]) l = mid + 1;
}
f[l] = min(arr[b[i]], f[l]);
}
}
cout << len << endl;
return 0;
}
图论
P4779 单源最短路径(标准版)
#include <iostream>
#include <cstring> // memset
#include <map>
#include <queue>
#include <vector> // 小根堆
using namespace std;
typedef pair<int, int> PII; // first: 距离,second: 节点编号
const int N = 1e5 + 10;
int n, m, s;
// h: head, w: weight边权,ne: next,dist: 距离,ver: 邻接表(链式前向星)
int h[N], w[2 * N], ver[2 * N], ne[2 * N], cnt;
int dist[N]; // s 点到所有点的最短路径
bool vis[N]; // 标记这个点是否已经出队
// 添加一条边
void add(int x, int y, int z) { // x -> y, 边权 z
ver[++cnt] = y;
ne[cnt] = h[x];
h[x] = cnt;
w[cnt] = z;
}
void dijkstra(int s) { // 主算法
memset(dist, 0x3f, sizeof(dist)); // 将所有距离初始化为无穷大
dist[s] = 0; // 起点到自己的距离为 0
priority_queue<PII, vector<PII>, greater<PII> > heap; // 定义小根堆
heap.push({0, s}); // s 点到 s 点距离为 0
while (heap.size()) {
// 取出堆顶元素并弹出
PII t = heap.top();
heap.pop();
int x = t.second, d = t.first;
if (vis[x]) continue; // 如果出过队,则不需要继续扩展
vis[x] = 1; // 设置标记数组
for (int i = h[x]; i; i = ne[i]) { // 扩展
int y = ver[i];
if (dist[y] > d + w[i]) {
dist[y] = d + w[i];
heap.push({dist[y], y});
}
}
}
}
int main() {
// 读入图
cin >> n >> m >> s;
for (int i = 1; i <= m; i ++) { // 读入每条边
int x, y, z;
cin >> x >> y >> z;
add(x, y, z); // 添加这条边
}
// 调用算法
dijkstra(s);
// 输出结果
for (int i = 1; i <= n; i ++)
cout << dist[i] << " ";
cout << endl;
return 0;
}
最小生成树(Kruskal)
int n, m; // 点数(连通块个数),边数
struct edge { // edge 边
int u, v, w;
} e[maxm];
int Find(int x);
void merge(int x, int y);
void kruskal() {
sort(e + 1, e + m + 1); // 边权从小到大排序
for (int i = 1; i <= m && n > 1; i ++) {
auto [u,v,w] = e[i]; // 结构化绑定(C++17,考试支持)
if (Find(u) != Find(v)) {
merge(u, v);
sum += w;
n --;
}
}
}
最小生成树(Prim)
int G[maxn][maxn]; // 邻接矩阵
for (int i = 1; i <= n; i ++) {
dis[i] = G[1][i];
}
used[1] = true;
for (int i = 1; i <= n - 1; i ++) {
int minval = INT_MAX, minid = 0;
for (int j = 1; j <= n; j ++) {
if (!used[j] && dis[j] < minval) {
minval = dis[j];
minid = j;
}
}
if (minval == INT_MAX) {
cout << "orz\n";
return 0;
}
used[minid] = 1;
sum += minval;
for (int j = 1; j <= n; j ++) {
if (!used[j] && G[minid][j] < dis[j]) {
dis[j] = G[minid][j];
}
}
}
B3644 拓扑排序(家谱树)
#include <bits/stdc++.h>
using namespace std;
int n;
vector<int> ver[105];
int ind[105];
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) {
int to;
while (cin >> to) {
if (to == 0) break;
ver[i].push_back(to);
ind[to] ++;
}
}
priority_queue<int, vector<int>, greater<int> > q;
for (int i = 1; i <= n; i ++) {
if (ind[i] == 0) q.push(i);
}
while (!q.empty()) {
int x = q.top();
q.pop();
cout << x << " ";
for (int i = 0; i < ver[x].size(); i ++) {
int y = ver[x][i];
ind[y] --;
if (ind[y] == 0) q.push(y);
}
}
return 0;
}
P1113 拓扑排序(杂务)
void tuopu_sort() {
queue<int> q;
for (int i = 1; i <= n; i ++) {
if (ind[i] == 0) {
q.push(i);
f[i] = len[i];
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto v: G[u]) {
ind[v] --;
if (ind[v] == 0) {
q.push(v);
}
f[v] = max(f[v], f[u] + len[v]);
}
}
}
洪水填充 flood fill
#include <iostream>
#include <map>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
char image[10][10];
int cx[] = {0, 0, 1, -1};
int cy[] = {1, -1, 0, 0};
bool is_valid(PII pt, int pcol, int ncol) {
int x = pt.first, y = pt.second;
return (1 <= x && x <= 8 && 1 <= y && y <= 8 && image[x][y] == pcol && image[x][y] != ncol);
}
void flood_fill(PII cur, int ncol) {
queue<PII> Q;
Q.push(cur);
int pcol = image[cur.first][cur.second];
image[cur.first][cur.second] = ncol;
while (!Q.empty()) {
PII pt = Q.front();
Q.pop();
for (int ch = 0; ch < 4; ch ++) {
PII npt = {pt.first + cx[ch], pt.second + cy[ch]};
if (is_valid(npt, pcol, ncol)) {
image[npt.first][npt.second] = ncol;
Q.push(npt);
}
}
}
}
int main() {
char new_color;
for (int i = 1; i <= 8; i ++) {
for (int j = 1; j <= 8; j ++) {
cin >> new_color;
image[i][j] = new_color;
}
}
PII cur = make_pair(4, 4);
cin >> new_color;
flood_fill(cur, new_color);
for (int i = 1; i <= 8; i ++) {
for (int j = 1; j <= 8; j ++) {
cout << image[i][j] << " ";
}
cout << endl;
}
return 0;
}
/*
Input:
gggggggg
ggggggrr
grrggrgg
gbbbbrgr
gggbbrgr
gggbbbbr
gggggbgg
gggggbbg
y
Output:
g g g g g g g g
g g g g g g r r
g r r g g r g g
g y y y y r g r
g g g y y r g r
g g g y y y y r
g g g g g y g g
g g g g g y y g
*/
Dijkstra + 堆优化(稀疏图)
struct node {
int to, dis;
bool operator < (const node &rhs) const {
return dis > rhs.dis;
}
};
int dis[maxn];
bool vis[maxn];
vector<int> G[maxn];
void dijkstra() {
fill(dis, dis + n + 1, INT_MAX);
dis[s] = 0;
priority_queue<node> q;
q.push({s, 0});
while (!q.empty()) {
auto [u, d] = q.top();
q.pop();
if (vis[u]) continue;
vis[u] = true;
for (auto v: G[u]) {
auto [to, d] = v;
if (dis[to] > dis[u] + d) {
dis[to] = dis[u] + d;
q.push({to, dis[to]});
}
}
}
}
Bellman-ford 算法求单源多汇最短路
// 太戈编程 589 武林大会
// 多源单汇最短路 -> 反边 -> 单源多汇最短路
// Bellman-ford 算法模板
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m;
ll peo_num[1005];
struct Edge {
ll u, v, w;
} arr[4005]; // 无向图边表加倍!
ll dis[1005];
void Bellman_ford() {
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
for (int ti = 1; ti < n; ti ++) { // 扫描需要执行 n-1 次!
for (int i = 1; i <= 2 * m; i ++) {
if (dis[arr[i].u] != 0x3f3f3f3f) {
dis[arr[i].v] = min(dis[arr[i].v], dis[arr[i].u] + arr[i].w);
}
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> peo_num[i];
cin >> m;
for (int i = 1; i <= m; i ++) {
cin >> arr[i*2-1].u >> arr[i*2-1].v >> arr[i*2-1].w;
arr[i*2].u = arr[i*2-1].v;
arr[i*2].v = arr[i*2-1].u;
arr[i*2].w = arr[i*2-1].w;
}
Bellman_ford();
ll ans = 0;
for (int i = 2; i <= n; i ++) {
if (dis[i] >= 0x3f3f3f3f -100) continue;
ans += dis[i] * peo_num[i];
}
cout << ans << endl;
return 0;
}
SPFA
void spfa() {
fill(dis, dis + n + 1, INT_MAX);
queue<int> q;
q.push(s);
dis[s] = 0;
inq[s] = 1;
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto [v, w]: G[u]) {
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!inq[v]) {
inq[v] = true;
q.push(v);
}
}
}
}
}
SPFA 判断负环
void negative_circle() {
fill(dis, dis + n + 1, INT_MAX);
queue<int> q;
q.push(s);
dis[1] = len[1] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (auto [v, d]: G[u]) {
if (dis[u] + d < dis[v]) {
dis[v] = dis[u] + d;
len[v] = len[u] + 1;
if (len[v] == n) {
puts("YES");
return;
}
if (!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
puts("NO");
}
Floyd 算法(多源最短路)
for (int k = 1; k <= n; k ++) {
f[k][k] = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
数学
同余方程求特解(exgcd)
int exgcd(int a, int b, int &x, int &y) { // x, y 传引用
if (!b) {
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, x, y);
int t = x, x = y;
y = t - (a / b) * y;
return d;
}
埃氏筛质数
int n;
bool pri[100005] = {};
void Init() {
memset(pri, 1, sizeof pri);
pri[0] = pri[1] = false;
for (int i = 2; i < n; i ++) {
if (!pri[i] || (long long) i * i >= n)
continue;
for (int j = i * i; j <= n; j += i)
pri[j] = false;
}
}
线性筛质数
ll n = 0, q = 0;
bool is_prime[100000010];
ll prime[10000010];
ll pcnt = 0;
void shai() {
memset(is_prime, 1, sizeof(is_prime));
is_prime[0] = is_prime[1] = 0;
for (int i = 2; i <= n; i ++) {
if (is_prime[i]) {
pcnt ++;
prime[pcnt] = i;
}
for (int j = 1; j <= pcnt and i * prime[j] <= n; j ++) {
is_prime[i * prime[j]] = 0;
if (i % prime[j] == 0) {
break;
}
}
}
return;
}
线性筛求最小质因子
typedef long long ll;
const ll N = 2e7 + 10;
ll pcnt = 0;
int pri[N], ispri[N], minpri[N], f[N];
void shai() {
for (int i = 2; i <= maxn; i ++) {
if (!ispri[i]) {
pcnt ++;
pri[pcnt] = i;
minpri[i] = i;
}
for (int j = 1; j <= pcnt && pri[j] * i <= N; j ++) {
ispri[i * pri[j]] = 1;
minpri[i * pri[j]] = pri[j];
if (i % pri[j] == 0) break;
}
}
}
高精度加法
const int N = 10500;
int na[N] = {0}, nb[N] = {0}, ans[N + 1] = {0};
string big_add(string a, string b) {
string sum;
memset(na, 0, sizeof(na));
memset(nb, 0, sizeof(nb));
memset(ans, 0, sizeof(ans));
for (int i = a.size(); i > 0; i --) na[i] = a[a.size() - i] - '0';
for (int i = b.size(); i > 0; i --) nb[i] = b[b.size() - i] - '0';
int maxlen = max(a.size(), b.size());
for (int i = 1; i <= maxlen; i ++) { // 模拟竖式加法
ans[i + 1] = (ans[i] + na[i] + nb[i]) / 10;
ans[i] = (ans[i] + na[i] + nb[i]) % 10;
}
if (ans[maxlen + 1] != 0) sum += "1"; // 防止最高位进位
for (int i = maxlen; i > 0; i --) {
sum += ans[i] + '0';
}
return sum;
}
高精度减法
const int N = 10500;
int na[N] = {0}, nb[N] = {0}, ans[N + 1] = {0};
string big_minus(string a, string b) {
string diff;
memset(na, 0, sizeof(na));
memset(nb, 0, sizeof(nb));
memset(ans, 0, sizeof(ans));
if ((a < b && a.size()) || b.size() > a.size()) { // 特判 b > a
return "-" + big_minus(b, a);
}
for (int i = a.size(); i > 0; i --) na[i] = a[a.size() - i] - '0';
for (int i = b.size(); i > 0; i --) nb[i] = b[b.size() - i] - '0';
int maxlen = max(a.size(), b.size());
for (int i = 1; i <= maxlen; i ++) {
if (na[i] < nb[i]) { // 模拟退位
na[i + 1] --;
na[i] += 10;
}
ans[i] = na[i] - nb[i];
}
while (ans[maxlen] == 0) maxlen --;
if (maxlen < 1) return "0";
for (int i = maxlen; i > 0; i --) diff += ans[i] + '0';
return diff;
}
高精度乘法
const int N = 10500;
int na[N] = {0}, nb[N] = {0}, ans[N + 1] = {0};
string big_times(string a, string b) {
string mul;
memset(na, 0, sizeof(na));
memset(nb, 0, sizeof(nb));
memset(ans, 0, sizeof(ans));
for (int i = 0; i < a.size(); i ++) na[i + 1] = a[a.size() - i - 1] - '0';
for (int i = 0; i < b.size(); i ++) nb[i + 1] = b[b.size() - i - 1] - '0';
int maxlen = a.size() + b.size();
for (int i = 1; i <= a.size(); i ++) {
for (int j = 1; j <= b.size(); j ++) {
ans[i + j - 1] += na[i] * nb[j];
}
}
for (int i = 1; i <= maxlen; i ++) {
ans[i + 1] += ans[i] / 10;
ans[i] %= 10;
}
while (ans[maxlen] == 0) maxlen --;
for (int i = max(1, maxlen); i >= 1; i --) {
mul += ans[i] + '0';
}
return mul;
}
高精度除法(高精除低精)
string big_div(string a, string b) {
string div;
memset(na, 0, sizeof(na));
memset(nb, 0, sizeof(nb));
memset(ans, 0, sizeof(ans));
for (int i = 1; i <= a.size(); i ++) na[i] = a[i - 1] - '0';
long long left = 0;
long long numb = 0;
for (int i = 0; i < b.size(); i ++) {
numb += (b[i] - '0') * pow(10, b.size() - i - 1);
}
for (int i = 1; i <= a.size(); i ++) {
ans[i] = (left * 10 + na[i]) / numb;
left = (left * 10 + na[i]) % numb;
}
long long first_ok = 1;
while (ans[first_ok] == 0 && first_ok < a.size()) first_ok ++;
for (int i = first_ok; i <= a.size(); i ++) div += (ans[i] + '0');
return div;
}
高精度取模(高精模低精)
long long big_mod(string a, string b) {
string div;
memset(na, 0, sizeof(na));
memset(nb, 0, sizeof(nb));
memset(ans, 0, sizeof(ans));
for (int i = 1; i <= a.size(); i ++) na[i] = a[i - 1] - '0';
long long left = 0;
long long numb = 0;
for (int i = 0; i < b.size(); i ++) {
numb += (b[i] - '0') * pow(10, b.size() - i - 1);
}
for (int i = 1; i <= a.size(); i ++) {
ans[i] = (left * 10 + na[i]) / numb;
left = (left * 10 + na[i]) % numb;
}
return left;
}
字符数组旋转九十度
char s[130][130];
void rotate(int &a, int &b) {
char tmp[130][130];
for (int i = 1; i <= a; i ++) {
for (int j = 1; j <= b; j ++) {
tmp[i][j] = s[i][j];
}
}
for (int i = 1; i <= a; i ++) {
for (int j = 1; j <= b; j ++) {
s[j][a - i + 1] = tmp[i][j];
}
}
swap(a, b);
}
二元一次不定方程
#include <bits/stdc++.h>
using namespace std;
long long exgcd(long long a, long long b, long long &x, long long &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
long long ans = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return ans;
}
int main() {
long long T;
cin >> T;
while (T--) {
long long a, b, c, d, x, y;
cin >> a >> b >> c;
d = exgcd(a, b, x, y);
if (c % d != 0) cout << -1 << endl;
else {
long long mix = (x%(b/d)+b/d)%(b/d)*(long long)(c/d)%(b/d);
if (mix == 0) mix = b / d;
long long miy = (y%(a/d)+a/d)%(a/d)*(long long)(c/d)%(a/d);
if (miy == 0) miy = a / d;
if (miy >= (c + b - 1) / b || mix >= (c + a - 1) / a) {
cout << mix << " " << miy << endl;
} else {
long long mxx = (c - miy * b) / a;
long long mxy = (c - mix * a) / b;
cout << (mxx-mix)/(b/d)+1 << " " << mix << " " << miy << " " << mxx << " " << mxy << endl;
}
}
}
return 0;
}
求特定组合数(懒标记)
long long c1(long long a, long long b) {
long long ans = 1;
long long lazytag = 1;
for (long long i = a, j = 2; ; i --, j ++) {
bool flag = false;
if (i >= a - b + 1) {
ans *= i;
flag = true;
}
if (j <= b) {
if (ans % lazytag == 0) {
ans /= lazytag;
lazytag = 1;
}
if (ans % j == 0) {
ans /= j;
} else {
lazytag *= j;
}
flag = true;
}
if (!flag) break;
}
ans /= lazytag;
return ans;
}
辗转相除求 gcd
long long gcd(long long a, long long b) {
if (b == 0) return a;
else return gcd(b, a % b);
}
预处理组合数
int c[2005][2005] = {};
c[1][1] = 1;
for (int i = 1; i <= 2000; i ++) {
c[i][0] = 1;
c[i][i] = 1;
}
for (int i = 2; i <= 2000; i ++) {
for (int j = 1; j < i; j ++) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % k;
}
}
快速幂+取模运算
ll quick_pow(ll a, ll b, ll p) {
ll ans = 1 % p;
while (b) {
if (b & 1) {
ans = (ll)ans * a % p;
}
a = (ll)a * a % p;
b >>= 1;
}
return ans;
}
计算特定区域内某因子个数
ll cntx(ll x) {
ll l = a, r = b;
ll ans = -1;
while (l != r) {
ans ++;
l /= x;
r /= x;
}
return ans;
}
计算几何
离散化扫描(扫描线)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
ll n, m;
struct Node { // 记录矩形
ll x, y1, y2; // 左上点、右边界
ll k; // 1 or -1:左边界(起点) 右边界(终点)
bool operator < (const Node &t) const {
return x < t.x; // sort 按照 x 升序排序
}
} rect[N * 2]; // rectangle 一个矩形左右边界 两倍空间
// 用线段树来维护 y 轴方向的区间信息
struct Tree {
ll l, r;
ll cnt; // 记录覆盖次数
ll len; // 覆盖长度(原始为浮点数)(y 轴方向)
} t[N * 8]; // 2 * 4 = 8
vector<ll> ys; // y 原始值过大且为浮点数,离散化
int find(ll y) { // 二分查找 离散化对应的值
// 注意:获取的下标从 0 开始
return lower_bound(ys.begin(), ys.end(), y) - ys.begin(); // 迭代器减法获取下标
}
void build(ll u, ll l, ll r) {
t[u].l = l, t[u].r = r;
if (l == r) { // 叶子节点
t[u].cnt = t[u].len = 0;
return;
}
ll mid = (l + r) >> 1;
build(u * 2, l, mid);
build(u * 2 + 1, mid + 1, r);
// 不需要 push_up
}
void pushup(ll u) {
// cnt 不为 0,被完全覆盖,区间有效,长度为离散化对应的原始值
if (t[u].cnt) t[u].len = ys[t[u].r + 1] - ys[t[u].l];
else if (t[u].l == t[u].r) t[u].len = 0; // 元区间(最小划分区间),未被覆盖,长度为 0
else t[u].len = t[u * 2].len + t[u * 2 + 1].len; // 区间部分覆盖,累加左右子区间的覆盖长度
}
void modify(ll u, ll l, ll r, ll k) {
if (l <= t[u].l && r >= t[u].r) {
t[u].cnt += k;
pushup(u);
return;
}
// 不需要 push_down
// 因为操作区间一定是成对出现的,只需要维护到父亲节点即可
// 查询的时候,也是到父亲节点就会返回
ll mid = (t[u].l + t[u].r) >> 1;
if (l <= mid) modify(u * 2, l, r, k); // 更新左子树
if (r > mid) modify(u * 2 + 1, l, r, k); // 更新右子树
pushup(u); // 将节点信息传递给父亲
}
int main() {
// freopen("atlantis.in", "r", stdin);
// freopen("atlantis.out", "w", stdout);
scanf("%lld", &n);
ys.clear(); // 多组数据,清空 vector
for (int i = 0; i < n; i ++) {
ll x1, y1, x2, y2;
scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
ys.push_back(y1);
ys.push_back(y2);
rect[i * 2] = {x1, y1, y2, 1}; // 起点边,偶数
rect[i * 2 + 1] = {x2, y1, y2, -1}; // 终点边,奇数
}
// 对 vector 进行离散化
sort(ys.begin(), ys.end());
ys.erase(unique(ys.begin(), ys.end()), ys.end()); // 去重 + 删除重复元素
m = ys.size(); // 存储 vector 大小
build(1, 0, m - 2); // m 条边对应 m - 1 个区间
sort(rect, rect + n * 2); // 对 x 进行升序排序,准备从左往右扫描
// 扫描开始
ll res = 0;
for (int i = 0; i < n * 2;) { // i 在函数内进行控制
int j = i;
while (j < n * 2 && rect[j].x == rect[i].x) {
j ++;
// 扫描所有 x 重复的边(在同一刻度上),j 表示下一条边
}
if (i) { // 第一条边不需要进行计算
res += t[1].len * (rect[i].x - rect[i - 1].x);
// 根节点记录的是扫描线总长度
}
while (i < j) {
// 上边界对应的区间不在统计范围内(M 条线 => M - 1 个区间)
modify(1, find(rect[i].y1), find(rect[i].y2) - 1, rect[i].k);
i ++;
}
}
cout << res << endl;
return 0;
}
数据结构
队列
#include <bits/stdc++.h>
using namespace std;
int main() {
int N = 0;
cin >> N;
queue <int> q;
while (N--) {
int op = 0;
cin >> op;
if (op == 1) {
int x = 0;
cin >> x;
q.push(x);
} else if (op == 2) {
if (q.size() == 0) {
cout << "ERR_CANNOT_POP" << endl;
} else {
q.pop();
}
} else if (op == 3) {
if (q.size() == 0) {
cout << "ERR_CANNOT_QUERY" << endl;
} else {
cout << q.front() << endl;
}
} else {
cout << q.size() << endl;
}
}
return 0;
}
栈
#include <bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
vector<unsigned long long> st;
int n = 0;
cin >> n;
for (int i = 1; i <= n; i ++) {
string op;
unsigned long long x;
cin >> op;
if (op == "push") {
cin >> x;
st.push_back(x);
} else if (op == "query") {
if (st.size() != 0) {
cout << st.back() << endl;
} else {
cout << "Anguei!" << endl;
}
} else if (op == "size") {
cout << st.size() << endl;
} else if (op == "pop") {
if (st.size() != 0) {
st.pop_back();
} else {
cout << "Empty" << endl;
}
}
}
}
return 0;
}
并查集
int f[maxn];
int find(int x) {
if (f[x] == x) return x;
else return f[f[x]] = find(f[x]);
}
void merge(int x, int y) {
int r1 = find(x);
int r2 = find(y);
f[r2] = r1;
}
bool isCon(int x, int y) {
return find(x) == find(y);
}
void init() {
for (int i = 1; i <= n; i ++) {
f[i] = i;
}
}
并查集(按秩合并)
int fa[maxn];
int sz[maxn];
int find2(int x) {
if (fa[x] == x) return x;
else return find2(fa[x]); // 按秩合并一般不进行路径压缩
}
void merge2(int x, int y) {
x = find2(x);
y = find2(y);
if (x != y) {
if (sz[x] < sz[y]) swap(x, y);
fa[y] = x;
sz[x] += sz[y];
}
}
P3374 树状数组1
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, m;
int a[N], t[N];
int lowbit(int x) {
return x & (-x);
}
// 单点修改
void add(int x, int y) { // 在 x 的位置加上 y
a[x] += y;
while (x <= n) {
t[x] += y;
x += lowbit(x);
}
}
// 查询前缀和
int ask(int x) {
int ans = 0;
while (x) {
ans += t[x];
x -= lowbit(x);
}
return ans;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) { // 初始化
int v;
cin >> v;
add(i, v);
}
while (m --) {
int p, x, y;
cin >> p >> x >> y;
if (p == 1) {
add(x, y);
} else {
cout << ask(y) - ask(x - 1) << endl;
}
}
return 0;
}
P3368 树状数组2
// 树状数组:区间操作,单点查询 -- 差分
// 用树状数组记录差分序列,查询时输出差分序列的前缀和 + 原序列初始值
#include <iostream>
using namespace std;
const int N = 5e5 + 10;
int n, m;
int a[N], t[N];
int lowbit(int x) {
return x & (-x);
}
int add(int x, int y) {
while (x <= n) {
t[x] += y;
x += lowbit(x);
}
}
int ask(int x) {
int ans = 0;
while (x) {
ans += t[x];
x -= lowbit(x);
}
return ans;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
while (m --) {
int p, x, y, k;
cin >> p;
if (p == 1) {
cin >> x >> y >> k; // [x, y] 差分 --> x, y + 1
add(x, k);
add(y + 1, -k);
} else {
cin >> x;
cout << ask(x) + a[x] << endl; // 加上原始值
}
}
return 0;
}
P3372 线段树1
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
struct SegmentTree{
int l, r;
long long sum, add;
#define l(x) t[x].l
#define r(x) t[x].r
#define sum(x) t[x].sum
#define add(x) t[x].add
} t[N * 4];
int a[N], n, m;
// 建树
void build(int p, int l, int r) {
l(p) = l, r(p) = r;
if (l == r) {
sum(p) = a[l];
return;
}
int mid = (l + r) >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
sum(p) = sum(p * 2) + sum(p * 2 + 1); // push_up
}
// 标记下传
void push_down(int p) {
if (add(p)) {
sum(p * 2) += add(p) * (r(p * 2) - l(p * 2) + 1);
sum(p * 2 + 1) += add(p) * (r(p * 2 + 1) - l(p * 2 + 1) + 1);
add(p * 2) += add(p); // 标记左右儿子
add(p * 2 + 1) += add(p);
add(p) = 0; // 清除父节点标记
}
}
void motify(int p, int l, int r, long long v) {
if (l <= l(p) && r >= r(p)) {
sum(p) += 1ll * v * (r(p) - l(p) + 1); // 更新当前节点的值
add(p) += v;
return;
}
push_down(p); // 下传标记
int mid = (l(p) + r(p)) >> 1;
if(l <= mid) motify(p * 2, l, r, v);
if (r > mid) motify(p * 2 + 1, l, r, v);
sum(p) = sum(p * 2) + sum(p * 2 + 1); // push_up
}
long long query(int p, int l, int r) {
if (l <= l(p) and r >= r(p)) return sum(p);
push_down(p); // 下传标记!
int mid = (l(p) + r(p)) >> 1;
long long val = 0;
if (l <= mid) val += query(p * 2, l, r);
if (r > mid) val += query(p * 2 + 1, l, r);
return val;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) scanf("%d", &a[i]);
build(1, 1, n);
while (m --) {
int op, l, r;
scanf("%d%d%d", &op, &l, &r);
if (op == 1) {
long long v;
scanf("%lld", &v);
motify(1, l, r, v);
} else {
printf("%lld\n", query(1, l, r));
}
}
return 0;
}
P3373 线段树2
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct SegmentTree{
ll l, r;
ll sum, add, mul;
#define l(x) t[x].l
#define r(x) t[x].r
#define sum(x) t[x].sum
#define add(x) t[x].add
#define mul(x) t[x].mul
} t[N * 4];
ll a[N], n, m, MOD;
// 建树
void build(ll p, ll l, ll r) {
l(p) = l, r(p) = r, mul(p) = 1;
if (l == r) {
sum(p) = a[l];
return;
}
ll mid = (l + r) >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
sum(p) = 1ll * (sum(p * 2) + sum(p * 2 + 1)) % MOD; // push_up
}
// 标记下传
void push_down(ll p) {
if (add(p) || mul(p) != 1) {
// 更新左子节点 sum 信息 (值 * 倍数 + 值 * 区间长度)
sum(p*2) = (sum(p*2) * mul(p) + add(p) * (r(p*2) - l(p*2) + 1)) % MOD;
// 右
sum(p*2+1) = (sum(p*2+1) * mul(p) + add(p) * (r(p*2+1) - l(p*2+1) + 1)) % MOD;
// 标记
mul(p*2) = mul(p*2) * mul(p) % MOD;
mul(p*2+1) = mul(p*2+1) * mul(p) % MOD;
add(p*2) = (add(p*2) * mul(p) + add(p)) % MOD;
add(p*2+1) = (add(p*2+1) * mul(p) + add(p)) % MOD;
mul(p) = 1;
add(p) = 0;
}
}
// 区间加法
void motify_add(ll p, ll l, ll r, ll v) {
if (l <= l(p) && r >= r(p)) {
sum(p) = (sum(p) + v * (r(p) - l(p) + 1)) % MOD; // 更新当前节点的值
add(p) = (add(p) + v) % MOD;
return;
}
push_down(p); // 下传标记
ll mid = (l(p) + r(p)) >> 1;
if(l <= mid) motify_add(p * 2, l, r, v);
if (r > mid) motify_add(p * 2 + 1, l, r, v);
sum(p) = (sum(p * 2) + sum(p * 2 + 1)) % MOD; // push_up
}
// 区间乘法
void motify_mul(ll p, ll l, ll r, ll v) {
if (l <= l(p) && r >= r(p)) {
sum(p) = sum(p) * v % MOD;
mul(p) = mul(p) * v % MOD;
add(p) = add(p) * v % MOD; // 乘法会影响已有的加法!
return;
}
push_down(p); // 下传标记
ll mid = (l(p) + r(p)) >> 1;
if(l <= mid) motify_mul(p * 2, l, r, v);
if (r > mid) motify_mul(p * 2 + 1, l, r, v);
sum(p) = (sum(p * 2) + sum(p * 2 + 1)) % MOD; // push_up
}
ll query(ll p, ll l, ll r) {
if (l <= l(p) and r >= r(p)) return sum(p);
push_down(p); // 下传标记!
ll mid = (l(p) + r(p)) >> 1;
ll val = 0;
if (l <= mid) val = (val + query(p * 2, l, r)) % MOD;
if (r > mid) val = (val + query(p * 2 + 1, l, r)) % MOD;
return val;
}
int main() {
scanf("%lld%lld%lld", &n, &m, &MOD);
for (int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
build(1, 1, n);
while (m --) {
ll op, l, r;
scanf("%lld%lld%lld", &op, &l, &r);
if (op == 1) {
ll v;
scanf("%lld", &v);
motify_mul(1, l, r, v);
} else if (op == 3) {
printf("%lld\n", query(1, l, r));
} else if (op == 2) {
ll v;
scanf("%lld", &v);
motify_add(1, l, r, v);
}
}
return 0;
}
字符串
KMP 字符串匹配
#include <iostream>
#include <cstring>
using namespace std;
char a[1000006], b[1000006];
int n, m;
int ne[1000006], f[1000006];
void get_ne() {
int j = 0;
for (int i = 2; i <= n; i ++) {
while (j > 0 and a[i] != a[j + 1]) j = ne[j];
if (a[i] == a[j + 1]) j ++;
ne[i] = j;
}
}
void get_f() {
int j = 0;
for (int i = 1; i <= m; i ++) {
while (j > 0 and (b[i] != a[j + 1] or j == n)) j = ne[j];
if (b[i] == a[j + 1]) j ++;
f[i] = j;
if (f[i] == n)
cout << i - n + 1 << endl;
}
}
int main() {
cin >> (b + 1) >> (a + 1);
n = strlen(a + 1);
m = strlen(b + 1);
get_ne();
get_f();
for (int i = 1; i <= n; i ++) {
cout << ne[i] << " ";
}
cout << endl;
return 0;
}
字符串哈希
// P3370 字符串哈希
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
ull base = 131; // 经验值,131进制不容易发生冲突(13331也是)
ull a[10010]; // 哈希数组
string s; // 字符串数组
ull n, ans = 1;
ull prime = 233317; // 处理冲突的方法:在哈希值上不断加上一个大质数直到新位置未被占用
ull mod = 212370440130137957ull;
ull hashe(string s) {
ull ans = 0;
for (char c: s) {
ans = (ans * base + ull(c)) % mod + prime;
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> s;
a[i] = hashe(s);
}
sort(a + 1, a + n + 1);
for (int i = 1; i < n; i ++) {
if (a[i] != a[i + 1]) ans ++;
}
cout << ans;
return 0;
}