废话

我们机房最近每天都是模拟赛,但我又打不过,于是我动起了歪主意。

我在机房偷偷看手机时,写了第一个版本的自动AC机。那一个版本只适用于Windows,所以老师发的结果中我爆零。但老师在给另一个同学重测时我是满分,并且重测的截图明显是来自Windows,所以我推测老师是在Linux下打点的。

从截图和数据包来看,测评软件明显是Lemon,所以我在GitHub上下载了Lemon的修改版LemonPlus。然后我改进了我的AC机,在Windows 10(TDM-GCC 4.9.2)和Ubuntu 19.04(GCC 8.3)测试通过。

最早我是按输入输出格式去读取数据,但这种方法比较难用。所以我在改进版当中按照字符读取所有输入数据,找到匹配的输入再按字符输出,并且不忽略标准答案中的空格等。

截图

Ubuntu

Ubuntu 测试截图

Windows

Windows 测试截图

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <fstream>
#include <sstream>
#include <string>

const char* probname = "**这里改成题目的名字**";
#ifdef _WIN32 // 如果是Windows,使用反斜线分割路径,否则用斜线分割路径
const char pathsep = '\\';
#else
const char pathsep = '/';
#endif
std::string get_filename(bool out, int id = 0) {
std::ostringstream str; // 使用标准库中的stringstream格式化路径
if (id == 0) {
str << probname << '.' << (out ? "out" : "in");
} else {
str << ".." << pathsep << ".." << pathsep << "data" << pathsep << probname << pathsep << id << '.' << (out ? "out" : "in");
}
return str.str(); // 返回值是string,在外面调用c_str方法,在内部调用c_str并返回const char*可能出错
}
bool check_stream(std::istream& fin, std::istream& tin) {
fin.seekg(0); // 要让比对的流回到开始点,被比对的流不需要,因为新打开的流就是从开始点开始的
for (char ch = fin.get(), ch2 = tin.get(); ch != -1 && ch2 != -1; ch = fin.get(), ch2 = tin.get()) { // 这里要两个流同时结尾时才停止比对,防止一个流是另一个流的前缀时出错,并且get方法出错时返回-1,并不会抛出异常,所以不会出错
if (ch != ch2) {
return false;
}
}
return true;
}
int main() {
std::ifstream fin(get_filename(false).c_str()); // C++的文件流,远比cin/cout+freopen快,就算是不与stdio同步的cin/cout也比fstream慢,还像cin一样好用
std::ofstream fout(get_filename(true).c_str());
for (int i = 1;; i++) {
std::ifstream tin(get_filename(false, i).c_str());
if (!tin.is_open()) { // 如果打不开文件,说明没有这个测试点(当然也可能是脸黑遇到了神秘bug),以后的测试点自然也没有,所以就可以不循环了。
break;
} else if (check_stream(fin, tin)) {
std::ifstream tout(get_filename(true, i).c_str());
for (char ch = tout.get(); ch != -1; ch = tout.get()) {
fout << ch;
}
return 0;
}
}
return 1; // main返回值非0会被Lemon视作运行时错误,也许可以用来判断你是否遇到了神秘bug。
}