常见小错误 FREQUENTLY MADE MISTAKES IN OI
1. 多测忘记清空
LCA的fa数组每次也要清空
BSGS 的哈希表
2. 数据溢出或取模出错
- 溢出
int相乘转long long,long long相乘转__int128
int a, b, c;
c = 1ll * a * b % M;
long long x, y, z;
z = x * y % M; // 会溢出!
// 正确写法
__int128 x, y, z;
z = x * y % m;
- 乘法(连乘每次都要取模),减法忘记取模
int a, b, c;
a = ((a - b) % M + M) % M; // 减法
a = 1ll * a * b % M; // 乘法
a = 1ll * a * b; // 乘法,乘1ll转long long防溢出
c = 1ll * a * b % M * c % M * ... * z % M; // 连乘
3. 使用STL或用数组模拟队列,栈等数据结构时忘记判空
4. 数位dp记忆化搜索版本,记忆化数组\(f\)是不考虑\(\text{high}\)和\(\text{lead}\)的结果
也要注意初始化
ll dfs(int u, ..., bool high, bool lead)
{
if (u < 1) {
...
return ...;
}
if (!high && !lead && f[u][cons][val] != -1) return f[u][cons][val]; // 注意是!high && !lead
...
if (!high && !lead) f[u][cons][val] = ret;
return ret;
}
5. 数组开小,注意算好数组大小
6. 注意运算优先级(尤其位运算)
| 类别 | 运算符 | 结合顺序 |
|---|---|---|
| 后缀 | () [] -> . ++ - - | 从左到右 |
| 一元 | + - ! ~ ++ - - (type)* & sizeof | 从右到左 |
| 乘除 | * / % | 从左到右 |
| 加减 | + - | 从左到右 |
| 移位 | << >> | 从左到右 |
| 关系 | < <= > >= | 从左到右 |
| 相等 | == != | 从左到右 |
| 位与 AND | & | 从左到右 |
| 位异或 XOR | ^ | 从左到右 |
| 位或 OR | | | 从左到右 |
| 逻辑与 AND | && | 从左到右 |
| 逻辑或 OR | || | 从左到右 |
| 条件 | ?: | 从右到左 |
| 赋值 | = += -= *= /= %= >>= <<= &= ^= |= | 从右到左 |
| 逗号 | , | 从左到右 |
摘自菜鸟教程
7. 递归边界忘记return
8. 线段树的范围下标不一定是\([1, n]\),也有可能是\([0, n-1]\)(例如当线段树建在离散化后的vector上)
9. 差分时应倒序遍历数组
for (int i = n; i >= 1; i--)
a[i] = a[i] - a[i-1];
10. 空元素的表示
对于运算而言,空元素应该理解为这个运算的单位元,即对于运算 \(\oplus\),有 \(a \oplus e = e \oplus a = a\)
例如,对于加法,空元素可用 \(0\) 表示,对于乘法,可以用 \(1\) 表示,对于矩阵乘法,可用单位矩阵 \(I\) 表示
11. 分块时注意下标指的是块编号还是原数组下标
12. 字典树中信息是存在边上的,而不是点上
13. 倍增数组第二维是位数时,注意防止越界
建议略开大一点
int fa[N+5][24];
for (int i = 24; i >= 0; i--) // RE 注意第二维下标最多23
fa[x][i] .....
14. 点分治
注意区分原树中传参得到的根节点,与选重心得到的新根节点
15. 题目中给出的图可能不连通
16. 局部变量记得初始化
17. 连通性相关
点双
注意特判仅有一个节点的点双
大多数题目,缩点后计算答案考虑度数为1、0的点双
点双缩点后至多有约\(2n\)个节点(原图为一条链)
18. 可持久化数据结构
每一个操作都对应一个唯一的根,且每个根都对应唯一的操作,不能多个操作对应同一个根
可持久化线段树更新时,记得从原来的版本继承数据
19. 循环中continue,记得将被跳过的必须操作处理完
20. 循环中判定无解,退出后立即判断
CF2150B
int sum = 0, ans = 1;
for (int i = mid, k = n % 2 ? 1 : 2; i > 1; i--, k += 2) {
if (k - sum < a[i]) {
puts("0");
flag = 1;
break;
}
ans = 1ll * ans * C(a[i], k - sum) % M;
sum += a[i];
}
// 这一行一定要加上!不然可能上面循环中输出一个0,下面if又输出一次
if (flag) continue; // !!!
if (n - sum != a[1]) {
puts("0");
flag = 1;
}
if (flag) continue;
21. cdq分治
-
归并排序仅需一个数组,不要与整体二分混淆
-
归并过程中统计数据,要统计从区间\(l\)开始总计数据
正确的:(cnt统计总和)
int cnt = 0;
for (i = l, j = mid + 1; j <= r; ) {
while (i <= mid && t2d[i].c <= t2d[j].c) {
if (!t2d[i].tag) cnt += w[t2d[i].id];
t3d[idx++] = t2d[i++];
}
if (t2d[j].tag) f[t2d[j].id] += cnt;
t3d[idx++] = t2d[j++];
}
错误的:(应新建变量mx统计\([l, i]\)的最大值)
for (i = l, j = mid + 1; j <= r; ) {
while (i <= mid && us[i].l <= us[j].l) {
tmp[len++] = us[i++];
}
if (i > l) {
maxl[us[j].id] = max(maxl[us[j].id], us[i - 1].l);
}
tmp[len++] = us[j++];
}
-
注意去重
-
注意排序规则,将能贡献给某一元素的其他元素都排在它前面(见下22)
22. 排序cmp函数
-
不能带等于号,严格弱序关系
-
如果有多个关键字,需要考虑当第一关键字相同时怎么做,是无需处理,还是应按第二关键字排序,原则是将能贡献给某一元素的其他元素都排在它前面
23. BSGS
哈希表记得多测清空
\[a^x \equiv b \pmod p
\]
特判\(a=0\),\(p=1\)的情况
\[x =
\begin{cases}
0, & p=1 \\
1, & p\neq 1,\ a=0,\ b = 0 \\
0, & p\neq 1,\ a=0,\ b = 1 \\
\text{use BSGS to calculate}, & \text{otherwise}
\end{cases}
\]

浙公网安备 33010602011771号