std::mt19937_64 mt_rnd((unsigned)time(0));
intR(int l, int r) {
return mt_rnd() % (r - l + 1) + l;
}
intmain() {
for (int i = 1; i <= m; i ++) {
int l = R(1, n), r = R(1, n);
if (l > r) std::swap(l, r);
printf("%d %d\n", l, r);
}
}
生成随机父向树:
std::mt19937_64 mt_rnd((unsigned)time(0));
intR(int l, int r) {
return mt_rnd() % (r - l + 1) + l;
}
intmain() {
printf("%d\n", n);
for (int i = 2; i <= n; i ++)
printf("%d %d\n", R(1, i - 1), i);
}
生成随机树(prufer 序列):
std::mt19937_64 mt_rnd((unsigned)time(0));
intR(int l, int r) {
return mt_rnd() % (r - l + 1) + l;
}
int n;
int prufer[N];
int deg[N];
std::pair<int, int> e[N];
intmain() {
for (int i = 1; i <= n - 2; i ++) prufer[i] = R(1, n);
for (int i = 1; i <= n; i ++) deg[i] = 1;
for (int i = 1; i <= n - 2; i ++) deg[prufer[i]] ++;
int leaf = 0, p = 0;
for (int i = 1; i <= n; i ++)
if (deg[i] == 1) { leaf = p = i; break; }
for (int i = 1; i <= n - 2; i ++) {
int x = prufer[i];
e[i] = std::make_pair(leaf, x);
if (-- deg[x] == 1 && x < p) {
leaf = x;
} else {
p ++;
while (deg[p] != 1) p ++;
leaf = p;
}
}
e[n - 1] = std::make_pair(leaf, n);
for (int i = 1; i < n; i ++)
printf("%d %d\n", e[i].first, e[i].second);
}
生成随机连通图(稀疏图):
先生成一棵 n−1 条边的随机父向树,保证连通。再生成剩下的 m−n+1 条边。
std::mt19937_64 mt_rnd((unsigned)time(0));
intR(int l, int r) {
return mt_rnd() % (r - l + 1) + l;
}
int n, m;
std::pair<int, int> e[M];
std::map< std::pair<int, int>, bool > exist;
intmain() {
for (int i = 1; i < n; i ++) {
e[i] = std::make_pair(R(1, i), i + 1);
exist[e[i]] = 1;
}
for (int i = n; i <= m; i ++) {
int x, y;
do {
x = R(1, n), y = R(1, n);
if (x > y) std::swap(x, y);
} while (x == y || exist[std::make_pair(x, y)]);
e[i] = std::make_pair(x, y);
exist[e[i]] = 1;
}
for (int i = 1; i <= m; i ++)
printf("%d %d\n", e[i].first, e[i].second);
}
对拍
Windows 系统命令 fc(或类 Unix 系统命令 diff)可以比较两个文件是否一致,一致返回 0,否则返回非零值。
clock() 可以返回当前程序已经运行的 CPU 时间,Windows 下单位 ms,Unix 下单位 s。
#include<bits/stdc++.h>intmain() {
for (int T = 1; T <= 10000; T ++) {
printf("Test #%d:\n", T);
system("gen.exe"); // 生成数据
system("std_1.exe"); // 运行程序 1
system("std_2.exe"); // 运行程序 2if (system("fc data1.out data2.out")) {
puts("WA");
return0;
} else {
puts("AC");
}
}
}
template <class &T>inlinevoidread(T &x) {
staticchar s;
staticbool opt;
while (s = getchar(), (s < '0' || s > '9') && s != '-');
x = (opt = s == '-') ? 0 : s - '0';
while (s = getchar(), s >= '0' && s <= '9') x = x * 10 + s - '0';
if (opt) x = ~x + 1;
}
typedeflonglong s64;
s64 qmul(s64 a, s64 b, s64 p) {
s64 ans = 0;
for (; b; b >>= 1) {
if (b & 1) ans = (ans + a) % p;
a = 2 * a % p;
}
return ans;
}
O(1) 快速乘:
注意到:
a×bmodp=a×b−⌊a×bp⌋×p
利用 long double 来处理 ⌊a×bp⌋。
虽然 a×b 和 ⌊a×bp⌋×p 的数值可能很大,但是两者的差一定在 [0,p) 之间,我们只关心它们的前 64 位即可,这正好可以用 long long 运算的自然溢出来处理。
typedeflonglong s64;
s64 qmul(s64 a, s64 b, s64 p) {
s64 c = (longdouble)a * b / p + 1e-8;
s64 ans = a * b - c * p;
if (ans < 0) ans += p;
if (ans >= p) ans -= p;
return ans;
}
快速幂
intqpow(int a, int b, int p) {
int ans = 1;
for (; b; b >>= 1) {
if (b & 1) ans = 1ll * ans * a % p;
a = 1ll * a * a % p;
}
return ans;
}
整数除法 上下取整
intnormal_dn(int x, int y) {
return x / y;
}
intnormal_up(int x, int y) {
return (x + y - 1) / y;
// return x % y == 0 ? x / y : x / y + 1;
}
intdiv_dn(int x, int y) {
bool flag = 0;
if (x < 0) x = -x, flag ^= 1;
if (y < 0) y = -y, flag ^= 1;
return flag ? -normal_up(x, y) : normal_dn(x, y);
}
intdiv_up(int x, int y) {
bool flag = 0;
if (x < 0) x = -x, flag ^= 1;
if (y < 0) y = -y, flag ^= 1;
return flag ? -normal_dn(x, y) : normal_up(x, y);
}
从小到大的倍增:记 p 表示上一次成功倍增段的末尾,len 表示当前倍增的步长(初始时为 1)。具体流程为:
尝试加入区间 (p,p+len]。
若加入区间 (p,p+len] 合法,则 p←p+len,len←len×2。
若加入区间 (p,p+len] 不合法,则 len←len/2。
重复上述流程,直到 len=0 时,结束倍增。
二叉树
n0 与 n2 的关系:n0=n2+1。
n0 与 n2 的关系证明:
n=n0+n1+n2=1+n1+2n2⇒n0=n2+1
贪心
贪心的常见证明方法:
微扰法(邻项交换)。
范围缩放法。
决策包容性。
反证法。
数学归纳法。
反悔贪心:待填。
排序
选择排序
每轮找出第 i 小的元素(即 ai∼an 中最小的元素),然后将其与数组的第 i 位交换。进行 n−1 轮即可完成排序。
时间复杂度 O(n2),不稳定。
voidselection_sort() {
for (int i = 1; i < n; i ++) {
int ith = i;
for (int j = i + 1; j <= n; j ++)
if (a[j] < a[ith]) ith = j;
std::swap(a[i], a[ith]);
}
}
冒泡排序
每轮检查相邻两个元素,若 ai>ai+1 则交换 ai,ai+1。经过 k 轮后,数组末尾的 k 个元素必然是最大的 k 个元素,故进行 n−1 轮即可完成排序。
时间复杂度为 O(n2),稳定。
每轮冒泡排序,对逆序对的影响:记 ci 表示以 i 结尾的逆序对个数,则 ci←max(0,ci+1−1)。
voidmerge_sort(int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r);
int i = l, j = mid + 1;
for (int id = l; id <= r; id ++)
if (i <= mid && (j > r || a[i] <= a[j]))
tmp[id] = a[i ++];
else
tmp[id] = a[j ++];
for (int id = l; id <= r; id ++)
a[id] = tmp[id];
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探