# genlib **Repository Path**: zealot27/genlib ## Basic Information - **Project Name**: genlib - **Description**: 快速生成洛谷测试数据文件的 C++ 库。 - **Primary Language**: C++ - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 8 - **Forks**: 1 - **Created**: 2022-04-22 - **Last Updated**: 2026-01-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # README.md ## 简介 `genlib.hpp` 是一个用于快速生成洛谷测试数据文件的 C++ 库。 --- ## 快速开始 将文件解压后,打开 `template.cpp` 文件。这是一个模板文件。 使用 $IDE$ 打开 `template.cpp` 文件,内容如下: ```cpp #include void func1(unsigned id) { // 输入数据生成函数 // 使用标准输入输出即可 // id 为测试点编号。可为不同测试点设置不同数据范围 } void func2() { // 输出数据生成函数 // 使用标准输入输出即可 } int main() { testcase ts("data", 20); // 文件名,总文件数 ts.generate(func1); ts.solve(func2); return 0; } ``` 其中,第 $21$ 行处的代码用于初始化所有的文件,第一个参数是指定的文件名,第二个参数是文件总数。最终生成的文件为按编号加上后缀,如 `data01.in` ,`data02.in` ,……,`data20.in` ,`data01.out` ,`data02.out` ,……,`data20.out` ,共计 $40$ 个文件。 **所有的生成的文件保存在相同目录下的 `data` 文件夹中**。如果 `data` 文件夹不存在则会自动创建。 另外,`func1` 函数用来生成输入数据,`func2` 函数用来生成输出数据(答案)。你只需要手动编写相应逻辑即可。 ### func1 函数 `func1` 函数 **必须** 包含一个参数 `id` ,用于获取当前的测试点编号。你可以根据不同的测试点设置不同的数据范围。 假如需要的输入数据格式如下: ``` 5 1 8 3 2 7 ``` 即第一行一个整数 $n$(数据范围 $1 \le n \le 1000$),而第二行共 $n$ 个整数 $a_i$ ,用空格隔开(数据范围 $0 \le a_i \le 10^9$)。你可以这样编写 `func1` 函数: ```cpp void func1(unsigned id) { integer _int1(1, 1000); // 随机数生成器一 integer _int2(0, 1e9); // 随机数生成器二 int n = _int1.tick(); // tick() 方法会生成一个区间内的随机数 cout << n << endl; while (n--) { cout << _int2.tick() << ' '; } } ``` 即按照需要的格式直接 **标准输出** 即可。 ### func2 函数 `func2` 函数用来生成答案文件。没有参数。 假如题目是求序列中的最大值,你可以这样编写 $func2$ 函数: ```cpp void func2() { int n; cin >> n int ans = 0; while (n--) { int x; cin >> x; ans = max(ans, x); } cout << x; } ``` 只需要使用标准输入输出正常的输入数据然后输出结果即可。 ### 编译并运行 编写好文件名,文件总数和两个函数之后,直接编译 `template.cpp` 并运行: `g++ -std=c++17 template.cpp` 编译需要使用 `c++17` 标准。 之后将生成的文件打包后直接上传洛谷即可。 --- # API ## [class] testcase 用于生成所有测试点数据文件的类。 ### 构造函数 `testcase varname(cosnt char* filename, cosnt unsigned &n)` `testcase` 类的构造函数,第一个参数为文件名,第二个参数为测试点个数。 例: ```cpp // 共 1 个测试点,文件名为 test // 每个测试点生成两个文件:test01.in, test01.out testcase ts("test", 1); ``` ### generate() 方法 `void generate(void (*func) (unsigned))` 接受一个函数为参数(必填)。用于生成所有的 `.in` 文件。 作为参数的函数必须包含一个 `unsigned` 类型的参数 `id` 。 ```cpp void func(unsigned id) { // ... } // ... ts.generate(func); ``` ### solve() 方法 `void solve(void (*func) (unsigned))` 接受一个函数为参数(必填)。用于生成所有的 `.out` 文件。 ```cpp void func() { // ... } // ... ts.solve(func); ``` ### config() 方法 `void config(void)` 用于生成测试点配置文件 `config.yml` 。 [洛谷的测试点配置文件规则](https://www.luogu.com.cn/blog/luogu/problem-config) ```cpp ts.config(); ``` ### set_timelimit() 方法 `void set_timelimit(const unsigned &v, unsigned s, unsigned t)` 用于设置编号从 $s$ 到 $t$ 的测试点的时间限制。单位为 $ms$ 。默认值为 $1000ms$ 。 如果不传递后两个参数,则默认为修改所有测试点。 ```cpp ts.set_timelimit(2000); // 设置所有测试点的时间限制为 2000 ms ts.set_timelimit(2000, 10, 20); // 设置测试点 10 到 20 的时间限制为 2000 ms ``` ### set_memorylimit() 方法 `void set_memorylimit(const unsigned &v, unsigned s, unsigned t)` 用于设置编号从 $s$ 到 $t$ 的测试点的内存限制。单位为 $kb$ 。默认值为 $128MB$ 。 如果不传递后两个参数,则默认为修改所有测试点。 ```cpp ts.set_memorylimit(256000); // 设置所有测试点的内存限制为 256 MB ts.set_memorylimit(256000, 10, 20); // 设置测试点 10 到 20 的内存限制为 256 MB ``` ### set_score() 方法 `void set_score(const unsigned &v, unsigned s, unsigned t)` 用于设置编号从 $s$ 到 $t$ 的测试点的得分。默认值为 $5pts$ 。 如果不传递后两个参数,则默认为修改所有测试点。 ```cpp ts.set_score(10); // 设置所有测试点的得分为 10 pts ts.set_score(10, 10, 20); // 设置测试点 10 到 20 的得分为 10 pts ``` ### set_ispretest() 方法 `void set_ispretest(const bool &v, unsigned s, unsigned t)` 用于 $CF$ 赛制题,设置编号从 $s$ 到 $t$ 的测试点是否为 $Pretest$ 测试点。默认值为 $false$ 。 如果不传递后两个参数,则默认为修改所有测试点。 ```cpp ts.set_ispretest(true); ts.set_ispretest(true, 10, 20); ``` ### set_subtaskid() 方法 `void set_subtaskid(const unsigned &v, unsigned s, unsigned t)` 用于设置编号从 $s$ 到 $t$ 的测试点的子任务编号。 如果不传递后两个参数,则默认为修改所有测试点。 ```cpp ts.set_subtaskid(1); // 设置所有测试点的子任务编号为 1 ts.set_subtaskid(2, 10, 20); // 设置测试点 10 到 20 的子任务编号为 2 ``` ## [class] rnd 所有随机值类的基类。 ### permute() 方法 `static std::vector permute(const size_t &n)` 静态方法,用于生成一个 $[1,n]$ 的排列。 返回值类型: `std::vector` 。 ```cpp vector p = rnd::permute(10); for (const auto &x : p) { cout << x << ' '; } // out: 1 6 8 2 3 9 4 10 5 7 ``` ## [class] integer $integer$ 类是 $rnd$ 类的一个子类。用于生成随机整数。 ### 构造函数 `template integer varname(const T &l, const T &r)` $integer$ 类的构造函数,用于生成 $[l,r]$ 范围内的随机整数。 默认的随机数范围为 $[-2147483647,2147483647]$ 。 ```cpp // 一个 integer 对象为一个整型随机数生成器,可生成的随机数范围为 [l, r] // 如果不设置上下界,则随机数范围为 [-2147483647,2147483647] // _int1 可生成的随机数范围为 [-100, 100] integer _int1(-100, 100); // _int2 可生成的随机数范围为 [-2147483647,2147483647] integer _int2; ``` ### tick() 方法 `template T tick(void)` $tick()$ 方法返回区间内的一个随机数。 ```cpp integer _int; integer _ll(-1e10, 1e10); cout << _int.tick() << endl; // 54 cout << _ll.tick() << endl; // -1928735 ``` ## [class] real $real$ 类是 $rnd$ 类的一个子类。用于生成随机实数。 ### 构造函数 `template real varname(const T &l, const T &r)` $real$ 类的构造函数,用于生成 $[l,r)$ 范围内的随机实数。 必须提供上下界。 ```cpp real f(1.0, 10.0); ``` ### tick() 方法 `template T tick(void)` $tick()$ 方法返回区间内的一个随机实数。 ```cpp real _f(1.0, 10.0); for (int i = 1; i <= 10; i++) { cout << fixed << setprecision(6) << _f.tick() << endl; } ``` 以上代码会输出 $10$ 个范围在 $[1.0,10.0)$ 之间的随机实数,保留 $6$ 位小数。 ``` 2.584082 5.744109 4.422239 2.330151 7.537425 9.529977 2.269839 9.318667 9.431893 1.888319 ``` ## [class] charset $charset$ 类是 $rnd$ 类的一个子类。用于生成随机字符。 ### 构造函数 `charset varname(const std::string &s)` $charset$ 类的构造函数接收一个字符串 $s$ 作为参数,以 $s$ 中出现的字符作为字符集来生成随机字符。 如果省略这个参数,则默认的字符集为 **小写字母** 。 最终结果会对 $s$ 中的字符进行去重。 ```cpp charset cs("0123456789!@#%"); // 构造一个由 0123456789!@#% 构成的字符集 charset small_letter(); // 构造一个由所有小写字母构成的字符集 ``` ### tick() 方法 `char tick(void)` $tick()$ 方法返回指定字符集内的一个随机字符。所有字符出现的概率相同。 ```cpp charset cs("0123456789!@#%"); for (int i = 1; i <= 10; i++) { cout << cs.tick(); } ``` 以上代码会随机输出 $10$ 个 $s$ 中包含的字符: ``` #25%73@9!% ``` ### str() 方法 `std::string str(const size_t &len)` $str()$ 方法生成一个长度为 $len$ 的随机字符串,其中所有的字符都来自指定的字符集。 ```cpp // ... charset cs; // 默认的字符集为所有小写字母 cout << cs.str(10) << endl; ``` 以上代码会输出一个长度为 $10$ ,仅由小写字母构成的随机字符串。 ## [namespace] graph ### tree() 函数 `void graph::tree(const unsigned &n, int l, int r)` - 说明 $graph::tree$ 函数用于生成一个 $n$ 个顶点的无根树。 第一个参数 $n$ 为顶点数。后两个参数 $l$ 和 $r$ 用于设置边权,表示边权的数据范围 $[l,r]$ 。 如果不需要边权,只需要传递第一个参数。 - 限制 顶点数 $n$ 的上限设置为 $100$ 万。超过 $100$ 万会抛出一个错误。 - 性能 生成 $100$ 万个顶点的带边权树用时不超过 $5000$ $ms$ 。 ```cpp #include "genlib.hpp" using namespace graph; void func(unsigned id) { // 10 个顶点的无根树 tree(10); } int main() { testcase ts("test", 1); // 文件名,总文件数 ts.generate(func); return 0; } ``` 以上代码会生成一个 $test01.in$ 文件,文件内容为: ``` 9 7 3 7 5 9 6 3 2 7 1 5 5 4 2 10 8 1 ``` 如果需要边权,可将边权的范围 $[l,r]$ 作为第 $2$ 个和第 $3$ 个参数传递: ```cpp void func(unsigned id) { // 10 个顶点,边权范围 [-100, 100] 的无根树 tree(10, -100, 100); } ``` 以上代码会生成一个 $test01.in$ 文件,文件内容为: ``` 6 3 -65 8 7 -75 4 10 -23 8 1 92 4 2 32 2 9 -20 9 1 -66 4 6 -54 9 5 23 ``` ### connected_graph() 函数 `void graph::connected_graph(const long long &n, const long long &m, int l, int r)` - 说明 $graph::connected\_graph$ 函数用于生成一个 $n$ 个顶点和 $m$ 条边的连通图。 第一个参数 $n$ 为顶点数,第二个参数 $m$ 为边数。后两个参数 $l$ 和 $r$ 用于设置边权,表示边权的数据范围 $[l,r]$ 。 如果不需要边权,只需要传递前两个参数。 - 限制 顶点数 $n$ 的上限设置为 $20$ 万。超过 $20$ 万会抛出一个错误。 边数 $m$ 的上限设置为 $100$ 万。超过 $100$ 万会抛出一个错误。 如果 $m\dfrac{n\times (n-1)}{2}$ 会抛出一个错误。 - 性能 生成 $20$ 万个顶点和 $100$ 万条边的的带权无向连通图用时在 $5000$ $ms$ 左右。 ```cpp #include "genlib.hpp" using namespace graph; void func(unsigned id) { // 10 个顶点,20条边,边权范围 [-100, 100] 的连通图 ucg(10, 20, -100, 100); } int main() { testcase ts("test", 1); ts.generate(func); return 0; } ``` 以上代码会生成一个 $test01.in$ 文件: ``` 7 6 26 9 5 35 9 4 -97 9 2 82 8 2 10 8 6 -98 5 1 16 10 2 -15 7 3 62 3 8 2 2 5 37 4 5 73 5 8 91 8 4 -25 10 8 96 7 1 3 3 10 26 2 3 -72 9 1 -61 4 10 -5 ``` ### dag() 函数 `void graph::dag(const long long &n, const long long &m, int l, int r)` - 说明 $graph::dag$ 函数用于生成一个 $n$ 个顶点和 $m$ 条边的有向无环图。 第一个参数 $n$ 为顶点数,第二个参数 $m$ 为边数。后两个参数 $l$ 和 $r$ 用于设置边权,表示边权的数据范围 $[l,r]$ 。 如果不需要边权,只需要传递前两个参数。 - 限制 顶点数 $n$ 的上限设置为 $20$ 万。超过 $20$ 万会抛出一个错误。 边数 $m$ 的上限设置为 $100$ 万。超过 $100$ 万会抛出一个错误。 如果 $m\dfrac{n\times (n-1)}{2}$ 会抛出一个错误。 - 性能 生成 $20$ 万个顶点和 $100$ 万条边的的带权有向无环图用时在 $5000$ $ms$ 左右。 ```cpp #include "genlib.hpp" using namespace graph; void func(unsigned id) { // 10 个顶点,20条边,边权范围 [-100, 100] 的有向无环图 dag(10, 20, -100, 100); } int main() { testcase ts("test", 1); ts.generate(func); return 0; } ``` 以上代码会生成一个 $test01.in$ 文件: ``` 3 2 -99 10 6 35 8 6 -72 8 5 34 8 2 25 3 4 -85 5 1 -22 9 7 -34 6 7 71 8 4 21 2 9 91 2 6 -54 1 7 -1 1 9 -3 10 4 22 5 9 -42 10 1 78 5 4 83 10 7 -15 3 8 -40 ```