c++算法竞赛技巧
前言
这里指的是语言上的一些技巧,有常见的,也有冷门的。有些比较实用,有些比较鸡肋,仅作简单记录,慢更。
宏定义简化代码
本文中出现的宏定义
using ll = long long;
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
使用auto简化代码
auto是c++11标准引入的关键字,可以简化冗长的类型声明,例如
unordered_map<int, int> mp{{1, 2}, {2, 3}}; // c++11列表初始化
unordered_map<int, int>::iterator i = mp.begin();
auto it = mp.begin();、
// c++17还可以struture binding
for (auto [a, b] : mp)
cout << a << ' ' << b << '\t'; // 2 3 1 2
使用tuple代替简单的结构体
比如我们需要对读入一组4关键字的元素并进行排序(c++11)
tuple <int, int, int, int> p[N];
fu(i, 1, n) {
cin >> a >> b >> c >> d;
p[i] = {a, b, c, d};
}
// 默认逐个元素比较
sort(p + 1, p + n + 1);
tuple比较麻烦的一点是获取元素的值不如结构体方便,可以使用get获取引用或者tie解包元素
// get的索引必须是常量
get<0>(p[1]) = 2;
// ignore是我们不需要的值
tie(a, b, ignore, ignore) = p[0];
库函数
cout << __gcd(2, -3); //最大公约数,gcc内建函数,输出-1
cout << gcd(-2, -3) << ' ' << lcm(2, 3); //需要c++17,输出1 6
前缀和 差分 范围赋值 累加
ll x[]{0, 1, 2, 3};
partial_sum(x + 1, x + 4, x + 1); // 前缀和
fu(i, 1, 3) cout << x[i] << ' '; // 1 3 6
cout << '\n';
adjacent_difference(x + 1, x + 4, x + 1); // 差分
fu(i, 1, 3) cout << x[i] << ' '; // 1 2 3
cout << '\n';
iota(x + 1, x + 4, 1e10); // 范围递增赋值
fu(i, 1, 3) cout << x[i] << ' '; // 10000000000 10000000001 10000000002
cout << '\n';
cout << accumulate(x + 1, x + 4, 0ll); // 30000000003 累加,不加ll会溢出
随机相关
srand(time(0)); // 初始化随机数发生器,可以用time(0)当种子
rand(); // 返回一个0到RAND_MAX之间的整数
// chrono是c++11的时间库,还有random_device就不展开了
auto seed = chrono::system_clock::now().time_since_epoch().count();
default_random_engine e(seed); // c++11随机数引擎
uniform_int_distribution<ll> r(1, 10); // 将随机数转化为均匀分布,其它还有正态分布等
cout << r(e) << '\n';
// 随机打乱序列
vector<int> v{0, 1, 2, 3};
shuffle(v.begin(), v.end(), default_random_engine(time(0)));
for (int i : v)
cout << i << ' ';