_rqy的OI代码风格规范
本文介绍在信息学竞赛(OI)中采用的代码编写规范,源自 _rqy 长期实践,并参考了 Google 代码风格与 Menci 的相关标准。
基础原则
#include指令必须位于文件最顶部,且仅允许一次。- 禁止使用
using namespace std;。如需使用特定名称,应写为using std::cout;等形式。 - 单行字符长度不得超过 80 个。
预处理指令
- 头文件顺序建议:先 C++ 标准库,再 C 标准库,最后是其他自定义或交互库。若为独立模块,其对应头文件应放在第一个
#include。 - 在竞赛环境下,所有
#include应按字典序排列。 - 统一使用尖括号格式:
#include <foo>,避免使用双引号。 - 多层嵌套的
#if #endif必须添加注释标明配对关系。 - 尽量避免宏定义(
#define),优先使用const、typedef、inline等更安全的替代方式。 - 所有预处理指令不得缩进,保持顶格。
缩进与空格
- 每个代码块使用 2 个空格进行缩进。
- 大括号不换行,始终与前一行内容在同一行。
- 需要加空格的情况:
- 二元运算符(如
+、=)两侧;逗号和分号除外。 - 逗号和分号后方(除非位于行尾)。
if、for等控制语句与左括号之间。do-while中的while与前一个右大括号之间;if-else中的else与前一个右大括号之间。- 所有左大括号左侧(根据不换行规则,左大括号不应出现在行首)。
?和:两侧,包括构造函数初始化列表中的冒号。- 类型声明中
*、&的左侧,例如:const int& a, int A(int*& a)。 - 花括号与其内部语句或初始化列表之间(若在同一行)。
- 常量成员函数的
const关键字两侧。
- 二元运算符(如
- 禁止加空格的情况:
- 小括号或中括号与其内部表达式之间。
- 函数名与左括号之间(包括声明、定义和调用)。
- 单目运算符(如
!、-、*、&、~)之后,或自增自减操作符与其操作数之间。 - 逗号和分号左侧。
- 类型中
*、&的右侧。 .、->、::两侧。operator与所重载运算符之间(运算符与参数列表之间也按此规则处理)。
- 长表达式可换行,运算符置于行首;缩进应使逻辑对齐清晰;高优先级子表达式建议加括号以防止歧义。
- 参数列表或初始化列表过长时可换行,逗号置于行尾,缩进四空格。
- 极短函数可写成一行(但总长度不可超过 80 字符),此时花括号内需有空格(空函数体
{}除外)。
示例代码
struct VeryLongStruct {
int veryLongMember, data;
VeryLongStruct(int x, int y, int z, int w)
: veryLongMember(x) {
this->data = y + z * w;
}
};
inline int min_val(int a, int b) { return a < b ? a : b; }
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int main() {
int var1 = 2, var2 = 23;
VeryLongStruct obj(
var1,
var1,
var2,
var2
);
printf("%d\n", obj.data);
return 0;
}
空行规则
#include与using之间不允许空行,之后必须空一行。- 一组常量定义前后应有空行。
- 函数或结构体定义前后应留空行(多个短小函数如
min、max可省略空行)。 - 全局变量定义组上下需空行。
- 语句间可根据逻辑意义适当插入空行。
- 禁止出现连续两个及以上空行。
函数定义规范
main函数必须返回int,且不可省略return 0;。- 类/结构体参数传递应优先使用引用,避免不必要的拷贝。
- 极简函数可写成一行(不超过 80 字符),花括号内需保留空格(空函数体除外)。
- 单个函数不宜过长(一般不超过 100 行)。
命名约定
- 常规命名采用驼峰法:变量小写开头,函数/类/结构体大写开头。
- 结构体或类的成员函数可小写开头。
特殊命名规则
main函数。- 变量可用单个小写字母命名。
- 全局数组名可使用一个大写字母加 0~2 位数字,如
A、T1、F01。 - 模板函数或通用工具函数:如
readInt、powMod。 - 算法缩写:如
KMP、CRT、NTT、CDQ。 - 简短的
inline函数:如min、upd(表示更新操作)。 - 常量使用大写字母:如
N、M。 - 临时变量可以下划线开头,如
_tmp。
完整示例
#include <algorithm>
#include <cctype>
#include <cstdio>
typedef long long LL;
inline int read_int() {
int res = 0, ch;
while (!isdigit(ch = getchar()));
do res = res * 10 + ch - '0';
while (isdigit(ch = getchar()));
return res;
}
const int MOD = 998244353;
const int GEN = 3;
const int MAXN = 200050;
inline LL pow_mod(LL base, int exp) {
LL ans = 1;
for ((exp += MOD - 1) %= (MOD - 1); exp; exp >>= 1, (base *= base) %= MOD)
if (exp & 1) (ans *= base) %= MOD;
return ans;
}
LL inv[MAXN];
int n;
void NTT(LL* arr, int len, int opt) {
for (int i = 1, j = 0; i < len; ++i) {
int k = len;
while (~j & k) j ^= (k >>= 1);
if (i < j) std::swap(arr[i], arr[j]);
}
for (int h = 2; h <= len; h <<= 1) {
LL wn = pow_mod(GEN, (MOD - 1) / h * opt);
for (int j = 0; j < len; j += h) {
LL w = 1;
for (int i = j; i < j + (h >> 1); ++i) {
LL tmp1 = arr[i], tmp2 = arr[i + (h >> 1)] * w;
arr[i] = (tmp1 + tmp2) % MOD;
arr[i + (h >> 1)] = (tmp1 - tmp2) % MOD;
(w *= wn) %= MOD;
}
}
}
if (opt == -1)
for (int i = 0; i < len; ++i)
(arr[i] *= -(MOD - 1) / len) %= MOD;
}
LL F[MAXN], G[MAXN];
LL T1[MAXN * 4], T2[MAXN * 4];
void multiply(LL* A, int n, LL* B, int m) {
int len = 1;
while (len <= n + m) len <<= 1;
for (int i = 0; i < len; ++i)
T1[i] = (i < n ? A[i] : 0);
for (int i = 0; i < len; ++i)
T2[i] = (i < m ? B[i] : 0);
NTT(T1, len, 1);
NTT(T2, len, 1);
for (int i = 0; i < len; ++i)
(T1[i] *= T2[i]) %= MOD;
NTT(T1, len, -1);
}
void solve(int l, int r) {
if (l == r - 1) {
F[l] = (l == 0 ? 1 : F[l] * inv[l] % MOD);
return;
}
int mid = (l + r) >> 1;
solve(l, mid);
multiply(F + l, mid - l, G, r - l);
for (int i = mid; i < r; ++i)
(F[i] += T1[i - l]) %= MOD;
solve(mid, r);
}
int main() {
n = read_int();
inv[1] = 1;
for (int i = 2; i <= n; ++i)
inv[i] = -(MOD / i) * inv[MOD % i] % MOD;
for (int i = 1; i <= n; ++i) {
scanf("%lld", &G[i]);
(G[i] *= i) %= MOD;
}
solve(0, n + 1);
for (int i = 1; i <= n; ++i)
printf("%lld\n", (F[i] + MOD) % MOD);
return 0;
}