做题须知与禁忌
1. 多测(多组数据评测)
- 注意看题!请注意本题是不是多测。而且通常题目中只有一句话,有时又藏得隐蔽,一定要细心看!
- 数据较多时,多测避免用 vector 以及基于 vector 的容器。因为多测一般要清空,而 vector 清空不会释放内存,这就导致内存爆炸。
- 注意清空!但是更要注意内存。 memset 函数效率很高,但是用了它没有调用过的内存就会被快速覆盖上给定的字节。于是乎不论数据多小内存都是占满了的。也许本来有部分分,现在全部爆了。
2. STL
- 关于 c++ STL deque,list,queue 与 stack。
- STL 容器迭代器坑
std::multiset::erase(type val)
当指定元素的值的时候,删除所有为这个值的元素。因此若只想删除其中一个,应使用:std::multiset::erase(std::multiset::find(type val))
。- 有时候用闭区间(即:\([1,n]\))时,unique 记得减一。比如:
int nend=unique(a+1,a+1+n)-a-1
。
3. 数据范围
- 通常来讲,\(20\) 以内复杂度是 \(O(2^n)\) 如状压;\(120\) 以内是 \(O(n^4)\) 但常数得足够小,比如 \(0.5\);\(300\) 以内是 \(O(n^3)\);\(2000\) 以内 \(O(n^2\log n)\);\(10000\) 以内 \(O(n^2)\);\(7\times 10^5\) 以内 \(O(n\log n)\);\(10^7\) 以内线性。通常不会有 \(O(\log n)\) 的题。
- 但是应注意出题人有时会故意把范围缩小让你摸不透算法,结果做出一个超麻烦的方法。比如一道 \(O(n)\) 的题数据范围却是 \(10^5\)。(也有可能是常数因子比较大。)
std::sort
一定要注意,定义仿函数或重载小于符号时 \(a<b\) 和 \(b<a\) 不能同时为真,即最终返回不出现等号。否则数据范围一大就 CE。
4. 标准库与拓展库
-
当应用到两个及以上命名空间的时候,避免使用
using namespace std
,其余名字空间不可暴露在全局中(即不声明)。使用::
来访问,比如__gnu_pbds::priority_queue<int> Q
。 -
用拓展库的时候,不要使用标头
<bits/extc++.h>
,会报错,原因和解决办法见这里。少使用<bits/stdc++.h>
,不好用!原因是它不是 Dev 的浅层文件,写代码的时候找不到,只有编译的时候找得到。
5. 平台特性
- UVa 多测不能用
while(scanf("%d",&n)!=EOF)
或者while(~scanf("%d",&n))
,会超时。只能用while(cin>>n)
。实际上,Uva 的 scanf 好像就有问题/kk。 - POJ 作为同样老的 OJ (
也被称作“破 OJ”),不使用-static
编译指令。
6. 头文件
<iostream>
在标准中没有规定要包含<cstdio>
。为了保险,请在声明头文件的时候把两个都带上。<vector>
<cmath>
这两个头文件内的一些函数和类在 DevC++ 中不需要声明可以过编译。但是一定要把它们俩带上,因为似乎除了 DevC++ 的其他编译器都过不了。
7. OI 赛场编译(转载自 LOJ 如何在不提供 NOIlinux 2.0 环境的考点避免编译错误)
- 用
#include<bits/stdc++.h>
(内含 OI 通常能用到的所有头文件)而不是逐个写头文件,以免实际上漏了头文件但本地环境自动补齐。 - 把整个程序装进自己的 namespace,以免和库中的名称冲突(比如
next
和pipe
) - 如果题目没有特别说明,编译选项尽量加上
-std=c++14
。如果这样不能编译(指的是编译器版本过低不支持 C++14 而不是你的程序编译错误!),就至少加上-std=c++11
。 - 编译选项加上 ``-Wall`,让编译器提醒一些常见错误,比如函数不写返回值。
- 编译选项加上
-fno-ms-extensions
(关闭一些和 msvc 保持一致的特性,例如,关闭后不标返回值类型的函数会报 CE 而不是默认为 int)。
示例程序:
#include<bits/stdc++.h>
using namespace std;
namespace my_namespace{
int main(){
int a,b;
cin>>a>>b;
cout<<a+b<<endl;
return 0;
}
};
int main(){
return my_namespace::main();
}
编译选项:
g++ a.cpp -o a -std=c++14 -O2 -Wall -fno-ms-extensions
8. 内存使用问题
通常我们申请新空间使用 new
而清理内存使用 delete
。(还有一种使用类的方法,但我不记得了……)
但是!有时候 new
会增加不必要的程序占用空间,而据说这是内存碎片造成的。
当你的程序中 delete
可能用不到或者很少用的时候,也许开一个内存池更好(能够防止炸空间)。
比如:
class Node {
Node() {}
};
Node *nodes;
Node* newNode() {return new Node();}
可以替换成:
class Node {
Node() {}
};
const int MAXSIZE=1e6+6;
Node nodePool[MAXSIZE];
Node *poolPointer=nodePool;
Node* newNode() {return poolPointer++;}
这样就避免炸空间了。