- chjshen 的博客
出题不背锅
- 2025-6-16 14:20:33 @
出题不背锅
以洛谷 GESP202503 八级 割裂为例
第一步,生成题面
建议使用markdown语法生成题面,markdown对 公式支持不错。
题面主要包括以下内容:
- 题目背景(有无都可)
- 题目描述
- 输入格式
- 输出格式
- 样例
- 数据规模 每一项建议用二级标题
样例可以使用代码块,比如
5
1 2 3 4 5
第二步, 生成STD
代码略,一定要对拍,确保std不出错,我习惯用a.cc
来命名正解代码。
第三步,数据生成器(generator)
根据题目要求生成对应格式的数据,我习惯用 data.cc 来命名,数据生成器和后面的SPJ、validator等都建议使用 testlib.h 来做
#include <bits/stdc++.h>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include "testlib.h"
using namespace std;
const int N = 1E3;
int t;
int n = 1e6, a = 1e5;
void makeTree()
{
vector<int> tr;
tr.emplace_back(1);
for (int i = 2; i <= n; i++)
{
int idx = rnd.next(0, (int)tr.size() - 1);
cout << tr[idx] << " " << i << endl;
tr.emplace_back(i);
}
}
int main(int argc, char* argv[])
{
if (argc > 1) t = atoi(argv[1]);
registerGen(argc, argv, 1);
//使用参数 t 来规定是第几个测评数据,然后根据t来生成不同大小或范围的数据
if (t <= 2)
n = 10, a = 0;
else if (t <= 3)
n = rnd.next(20, 100), a = rnd.next(20, 100);
else if (t <= 4)
n = 100, a = 100;
else if (t <= 9)
n = rnd.next(10000, n), a = rnd.next(1000, (int)min((long long)(n - 1) * n / 2, (long long)a));
cout << n << " " << a << endl;
// 生成树
makeTree();
//好点对
map<pair<int, int>, int> mp;
for (int i = 1; i <= a; i++)
{
int u = rnd.next(1, n), v = rnd.next(1, n);
while (u == v || mp.count(make_pair(u, v)) )
u = rnd.next(1, n), v = rnd.next(1, n);
mp[make_pair(u, v)] = 1;
cout << u << " " << v << endl;
}
// 坏节点对
int u = rnd.next(1, n), v = rnd.next(1, n);
while (u == v) u = rnd.next(1, n), v = rnd.next(1, n);
cout << u << " " << v << endl;
return 0;
}
第四步,数据校验器(validator)
用来验证生成的测评输入数据是否正确,这一点很重要,尤其是字符串有关的题目要注意 windows 和 linux 下 换行的不同。
#include <bits/stdc++.h>
#include "testlib.h"
using namespace std;
vector<int> fa;
int find(int x) { return (fa[x] == x ? x : fa[x] = find(fa[x])); }
bool isTree(vector<pair<int, int>> &tr, int n)
{
// n-1条边
if (n - 1 != tr.size()) return 0;
fa.resize(n + 1);
for (int i = 1; i <= n; i++) fa[i] = i;
// 是不是有环
for (auto &e : tr)
{
int ra = find(e.first);
int rb = find(e.second);
if (ra == rb) // 有环
return 0;
fa[ra] = rb;
}
// 是不是连通
int root = find(1);
for (int i = 1; i <= n; i++)
{
int ra = find(i);
if (root != ra) return 0;
}
return 1;
}
int main(int argc, char *argv[])
{
registerValidation(argc, argv);
int n = inf.readInt(1, (int)1e6, "n");
inf.readSpace();
int a = inf.readInt(0, (int)1e5, "a");
inf.readEoln();
vector<pair<int, int>> tr;
for (int i = 1; i < n; i++)
{
int u = inf.readInt(1, n, "u");
inf.readSpace();
int v = inf.readInt(1, n, "v");
tr.emplace_back(make_pair(u, v));
inf.readEoln();
}
ensuref(isTree(tr, n), "not a tree!");
for (int i = 1; i <= a; i++)
{
int x = inf.readInt(1, n, "x");
inf.readSpace();
int y = inf.readInt(1, n, "y");
ensuref(x != y, "not eq!");
inf.readEoln();
}
int x = inf.readInt(1, n, "b_u");
inf.readSpace();
int y = inf.readInt(1, n, "b_v");
ensuref(x != y, "not eq!");
inf.readEoln();
// string n = inf.readToken("0|[1-9][0-9]{0,9}", "n");
// ensure(n.find("0") != string::npos);
// ensuref(1 == 2, "%d == %d error", 1, 2);
//
// inf.readSpace();
// inf.readInt(1, 9, "k");
// inf.readEoln();
// InStream nStream(inf, n);
// nStream.readInt(0, 2000000000, "n");
inf.readEof();
}
第五步,生成测评数据
如果中间有错误,及时返回前面的步骤进行修改。
#include <cstdio>
#include <cstdlib>
#include <iostream>
using namespace std;
char f[200];
int num = 10;
int main(int argc, char *argv[])
{
// 参数1为生成测评点的数量
if (argc > 1) num = atoi(argv[1]);
if (system("g++ -std=c++17 a.cc -o a"))
{
cout << "compile std program error, exit!" << endl;
return 0;
} // std标程
if (system("g++ -std=c++17 data.cc -o data")) // gen生成数据
{
cout << "Compile data generator error, exit!" << endl;
return 0;
}
if (system("g++ -std=c++17 validator.cc -o v")) // vallidator 数据校验器
{
cout << "Compile validator error, exit!" << endl;
return 0;
}
double st, en, ts, te;
for (int i = 1; i <= num; i++)
{
ts = clock();
snprintf(f, sizeof f, "./data %d > %02d.in", i, i);
st = clock();
if (system(f))
{
cout << f << ", make data error, exit" << endl;
return 0;
}
en = clock();
// 数据校验
snprintf(f, sizeof f, "./v < %02d.in", i);
if (system(f))
{
cout << "data validator error, exit!" << endl;
return 0;
}
cout << i << ": make data need " << en - st << "ms,";
snprintf(f, sizeof f, "./a < %02d.in > %02d.ans", i, i);
st = clock();
system(f);
en = clock();
te = clock();
cout << "make ans need " << en - st << "ms, total:" << te - ts << "ms, "
<< " OK!" << endl;
}
return 0;
}
第六步,验题
这一步也是不出锅的重要的一步,让别人来验一验你出的题,可能有你想不到的错误存在。