浅浅记录一下自己在算法竞赛中的注意事项。
数据类
注意看数大小,数学库中的函数尽量加上 * 1.0
,转成double,防止整型溢出。 ,int
型相乘如果可能溢出,乘 * 1LL
。
数据范围大于1e6,注意用快读。
浮点数输入输出:
cpp
少用float
scanf("%lf", &d);
printf("%.f",d)
取模,注意取成负数的情况。
全int
,但是数据太大,全转long long
。
cpp
#include <iostream>
using namespace std;
#define int lnog long
signed main() // 注意 int -> signed
{
}
行末无空格
cpp
cout<<data<<" \n"[i == n];
数据存储尽量不要自定义struct或者class,善于使用pair,array等,防止需要重载什么的,导致代码层面的错误。
多组注意清空。
树结构注意单边和双边。
STL
熟悉stl的数据结构,string, map, set,queue, stack, priority_queue, vector,array等。
熟悉stl的算法函数
cpp
lower_bound()
upper_bound()
find()
count()
substr()
*max_element()
sort()
unique()
在c++11中,max和min函数可以多个值。
cpp
max({v1,v2,v3,v4})
优先队列的重载
cpp
// 用priority_queue 自定义堆 http://www.cbww.cn/news/37826.shtml
// 要重载 < 操作符 ,注意两个const才可以通过编译
// 方法一 重载运算符<
struct adt { // 小顶堆
int a;
bool operator<(const adt& rhs) const { // 优先队列的><与sort的><相反. ** 没有const会报错
return a > rhs.a; // 这里 从大到小进行排序,队列从最右边开始,所以是小顶堆
}
};
// 方法二 使用lambda表达式
void test_priority_queue() {
auto cmp = [](int pre, int suf) { return pre > suf; }; // 小顶堆
priority_queue<int,vector<int>, decltype(cmp)> pq(cmp); // decltype 类型说明符
// 实现自定义PII堆结构
auto pii_cmp = [](PII pre, PII suf) {return pre.vf < suf.vf; };
priority_queue<PII, vector<PII>, decltype(pii_cmp)> heap(pii_cmp);
}
<bitset>
* 是由int型拼接的, e.g. 1000位bitset 操作时间复杂度 O( 1000 / (大于等于 32))
熟悉运用pair<int,int>
,vector
vector
重新赋值
cpp
vector<int> ve;
ve.assign(N,3)
杂
整数取整,可以用(LL)(ceil(a / b))
,也可以用a / b + (a % b == 0 ? 0 : 1)
。
lambda表达式的使用:
- 自定义排序
cpp
sort(all(ve), [](int pre, int suf) {
return pre > suf; // 从大到小
});
// 等价于
sort(all(ve), greater<int>());
- 写函数
cpp
auto lam = [&](int a) -> int {
if(a > 0) return 1;
else if(a == 0) return 0;
else return -1;
}
注意lambde递归用法,c++11可以用
cpp
functional<void(int)> dfs = (int u) {};
c++14可以用
cpp
auto dfs = [&](auto &&dfs, int u) -> void {};
创建数组,个人常用vector
cpp
vector<vector<int>> f(n, vector<int> (n, 1));
算法代码实现
个人算法模板整理:2022/Algorithm__Template at main · golitter/2022 (github.com)
不定项输入
cpp
// 需要包含 <sstream>
stringstream put_str;
string str;
getline(cin, str);
put_str<<str;
int cnt = 0,p;
while(put_str>>p) cnt++;
二分答案
- 最大值最小
cpp
int l, r;
while(l < r) {
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
- 最小值最大
cpp
int l, r;
while(l < r) {
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
去重离散化
cpp
vector<int> a,id,last;
id = a;
sort(id.begin(), id.end());
id.erase(unique(id.begin(), id.end()), id.end()); // 去重
for(int i = 0; i < n; ++i) {
last[i] = lower_bound(id.begin(), id.end(), a[i]) - id.begin();
}
建图
- 链式前向星
cpp
// 链式前向星
int h[N]; // 链表头,初始为-1 memset(h, -1, sizeof(h));
int e[N]; // 链表内容
int ne[N]; // 链表中指向下一个元素的指针
int w[N]; // 链表内容的权重
bool vis[N];
int idx; //
// <u , -- c -- , v> ( u --- w --> v
void add(int u, int v, int c) {
e[idx] = v, w[idx] = c, ne[idx] = h[u], h[u] = idx++;
}
- vector<pair<int,int>> 或者 vector<array<int,2>>
cpp
vector<vector<int>> g(n + 1); // 无权重w
vector<vector<pair<int,int>>> g(n + 1); // 有权重
时间复杂度
1e8
大概1秒。
注意调和级数等反直觉时间复杂度。
注意根据给的数据范围和特殊性猜解法。
刷题策略
30分钟没有思路就可以看题解了,不能没有思路就看题解。
刷题 + 写题解 提高较快,便于复习(虽然不复习
每次模拟赛要有总结和反馈。
平时要注意找到自己模拟赛时的不好的状态和好的状态,进行加强或减少。比如,我就是做题,想出来一点就去敲代码,之后再想剩下的算法。其实这是很不对的,算法竞赛主要考察的算法而不是什么代码,目前也在一直减少这个状况发生。
就算自己AC了题,也不要忘了去看看大佬们的代码,可能他们更加简洁,可以学学不同的思路等。