先贴自己的缺省源
#include<bits/stdc++.h>
using namespace std;
#define rd(i,n) for(int i=0;i<n;i++)
#define rp(i,n) for(int i=1;i<=n;i++)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=b;i>=a;i--)
#define st string
#define vt vector
#define pb push_back
//#define int long long
typedef long long ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
return 0;
}
//Crayan_r
万能头,一点 for
循环和长数据类型的 #define
和 typedef
关同步流习惯只关 cin
。
关于 #define int long long
个人很反感这个东西,但是也不否认它在调试时的一些用处。
首先的坏处就是空间,不太想争论 64 位机下时间 int32
是否和 int64
等同,只能说这个问题很复杂,不能直接说“相等”,涉及寻址、操作和总线传输若干操作之间的优劣不一,是计算机底层原理,追求这个不如去写“指针的指针的指针的指针”。
还有违背了 C++
本身的约定有一种违背天条的感觉,并且 int
默认 \(32\) 位是为了兼容以前程序和方便程序员之间合并调试代码作出的规定,毕竟还是现在的主流。
最后就是真正的硬伤,在 \(\text{Topcoder}\) 和很多国际比赛中(包括曾经在 \(\text{CNOI}\) 中的交互题),都采用函数式交互的方式避免输入输出常数问题,但是有些(例如 \(\text{Topcoder}\))评测系统不会在答案文件中寻找强制类型转换后契合输入格式的函数,例如题目要求 void f(int a)
,写 void f(long long a)
就不会被评测系统检测到从而造成 CE,这在本地是测不出来的,因为本地自动调用会强转,没人会在赛时去写一个 xxx.h
的评测系统。
但是我的缺省源中依旧常年带着 //#define int long long
,因为在调试不知道哪里 WA 的时候随手打开来跑一下确定是不是边角炸 int
真的很好用。不过我一般会在找到问题之后再关掉,思考 int
是否会炸是编程中重要思维的体现。
关于代码封装
class
,struct
,namespace
别的不说,class
真香!尤其是在题目需要写好多棵(甚至 \(n\) 棵)主席树或者平衡树的时候,可以派上大用场。在写数据结构的时候,我的态度是能封则封,而且可以避免不同数据结构之间常用变量名重合的问题(例如 tree[]
)。
namespace
我主要用来写部分分包和封装网络流。网络流是我唯一习惯封装的非数据结构算法,因为其中有很多例如 vis
和 dep
还有图之类很常用的东西,有点难度的网络流题又基本都带点别的东西,所以网络流的封装才对我形成了习惯。
而且网络流封装还有一个好处就是契合网络流题目的模型化,把一种问题转化成标准的网络流问题,只用有限且单一的网络流工具接口就可以解决问题,保留了网络流建模这类题目本身的美感。
我比较习惯用 struct
的有矩阵和次大值。原因是 struct
相对 class
比较轻量级,而且写重载运算符比较方便。在矩阵中直接用 *
代替 multi(x,y)
,在次大值里直接维护 v
和 sv
,用 +=
往里面添加新数,用 +
来合并。在维护这些东西的大数据结构上好处最为明显。
曾经还迷过指针实现的数据结构,用 ->
直接操作并且不用考虑大小真的很爽,而且指针在很多时候很快。一开始了解到这种写法就是 CF19D 树套树被卡常,换成指针写法就过了。
但是后来知道 new
虽然全局动态非连续空间,但是在没有内存池的情况下很慢,开内存池又没有了不用考虑大小的优势。直接操作的弊端在学到区间修改线段树的时候就显露了出来——因为默认在本对象修改,当前函数没有任何和当前对象是谁有关的信息,pushdown
我不会了。虽然后来又会了,但是也学到了新的东西——基于旋转的平衡树。旋转的代码在指针的情况下会变得非常长、不直观、难背且易错。所以也就渐渐抛弃了