luogu#B3785. [语言月赛202306] std::cerr
[语言月赛202306] std::cerr
题目背景
这是一道 hack 题。在本题目中,你将得到两个问题和两个解决对应问题的代码,但是给出的代码不能对于某些输入给出正确的输出。不能给出正确的输出的情况包括:
- 输出错误的结果。
- 运行超时。
- 产生一些运行时未定义行为。目前技术可检测的未定义行为仅包括数组越界。
对于这一问题,你需要提交一份符合要求的输入数据,使得给定的代码不能给出正确的输出。你可以直接使用『提交答案』功能,也可以提交一份以任何语言写成的数据生成器。
每道题占 分,本题共 分。
提示:如果你使用提交答案功能,请在提交其他题目时记得将语言改回你所使用的语言。
题目描述
以下给出两个问题的题目描述:
问题 1
给出整数 ,求 的值,其中 表示不小于 的最小整数。
问题 2
std::cerr
是一个输出到标准错误流的输出工具。如果你运行如下程序:
#include <iostream>
int main() {
std::cerr << "Hello Luogu!" << std::endl;
}
你会发现有 被打印在了命令行 /cmd 中;但是,如果你在 main
函数开头加入 freopen("text.txt", "w", stdout)
,再运行这个程序,然后打开 text.txt
,会发现文件是空的,而 仍然被打印到了命令行 /cmd 中。
由此,std::cerr
在算法竞赛中有了一个重要应用:使用 std::cerr
输出中间变量,变量会被打印到屏幕上,但却不会被打印到输出文件/标准输出中。同时,std::cerr
有刷新缓冲区的作用。以往关闭流同步代码一旦产生 Runtime Error,在这之前被打印到 stdout 的内容如果仍在缓冲区,则不会被输出,而使用 std::cerr
则不会有这个问题,因为每次完成输出后都会将缓冲区内容直接刷新到标准错误流中。
例如,你可以尝试在本地运行如下代码:
#include <iostream>
int main() {
std::ios::sync_with_stdio(false);
std::cout << "Hello Luogu!\n";
int x = *((int*)0);
}
对于大多数机器,屏幕上什么都不会打印,应用程序就会异常终止,这种情况是不利于调试的,因为无法二分出导致程序终止语句是哪一条。但是,如果你将上文中的 std::cout
换成 std::cerr
再尝试运行这一程序,就会发现在程序异常终止前, 被打印到了屏幕上。需要注意的是,刷新缓冲区是一件速度很慢的事情,如果刷新次数太多,会导致程序超时。
在刚结束的 ICPC 山东省赛中,扶苏试图使用 std::cerr
来完成对 K 题的调试,但是在四小时 59 分提交的代码中,扶苏本已经写出了正确的代码,但却忘记了删除 std::cerr
的调试语句,虽然这不会导致她向选手输出中输出额外信息,但因为刷新缓冲区次数过多,她本次提交被判定为 TIME LIMIT
,与省赛冠军失之交臂。
(计算可得,如果第四发 K 题的 attempt 通过,『四小时下班』将以 分钟罚时的优势绝杀夺冠)
因此,对于一个字符串,她觉得如果这个字符串含有 std::cerr
作为子串,则她觉得这个字符串『不太行』,否则她觉得这个字符串『非常行』。
给定 个字符串,请你判断它们是不是『不太行』。
输入格式
问题 1
输入只有一行三个整数,依次表示 ,,。
问题 2
第一行是一个整数,表示输入字符串个数 。
接下来 行,每行一个字符串,表示需要你判断的字符串。
在问题 中,输入不能有行末空格。但可以有文末回车。
输出格式
问题 1
输出一行一个整数表示答案。
问题 2
输出一行一个整数,表示『不太行』的字符串数量。
9 8 5
3
3
std::cerr
std::cerr<<"HelloLuogu!";
std::cout<<"HelloLuogu!";
2
提示
样例组与实际输入的说明
两个样例分别对应两个问题的样例输入输出。
如果你直接采用『提交答案』的方式,请分别将两个输入数据命名为 1.in
、2.in
,并打成 zip 压缩包进行提交;
如果你采用提交数据生成器的方式,你的生成器可以从标准输入读入一个整数 ,满足 ,表示该测试点对应的问题编号,然后输出对应的输入数据。
显然,你的程序不应该读入『输入格式』里提到的任何内容(而应该构造它们),也不应该输出『样例输出』里提到的任何内容(而是只输出你构造的输入数据)。你不应该使用样例测试你的程序,这只是对两个问题的样例说明。
数据规模要求
你给出的数据必须满足如下要求:
- 完全符合『输入格式』的规定,不能有多余的输入,但是可以有文末回车,问题 可以有行末空格。
- 对于问题 ,,。
- 对于问题 ,,输入字符串的总长度不超过 ,输入的任何字符串不能为空串,且只能含有可见字符(ASCII 值在 到 之间,空格不算可见字符)。
目标代码
你需要 hack 如下的代码:
问题 1
#include <iostream>
using namespace std;
int main() {
int a, b, c;
cin >> a >> b >> c;
int d = b - c;
cout << ((a + d - 1) / d) << endl;
}
问题 2
#include <iostream>
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int T;
std::string s;
int ans = 0, cur = 0;
for (std::cin >> T; T; --T) {
std::cin >> s;
++cur;
if (s.find("std::cerr") != std::string::npos) {
std::cerr << cur << '\n';
++ans;
}
}
std::cout << ans << std::endl;
}
目标代码的编译选项为 -std=c++14 -fno-asm -O2
。编译器为洛谷提供的 g++。你可以在『在线 IDE』中选择 C++14 语言来获得完全相同的编译环境。
数据判定
你给出的数据必须完全符合『数据规模要求』,否则将得到 Unaccepted 的结果。
超时判定
程序每执行若干条指令,我们会检测一遍程序的运行时间。我们保证两次检测之间的指令条数是一个输入规模无关的量,也即每执行 条指令会进行一次检测。且两次检测之间的指令条数不会太多,一般不超过 条 C++ 语句。
如果程序的运行时间超过 ,则判定为程序运行超时,返回 accepted 结果。
结果错误判定
如果程序在规定的时间内结束且给出了一个输出,我们会比较这个输出和完全正确的输出,如果二者不同,则判定为 hack 成功,返回 accepted 结果。
未定义行为判定
我们会在每次显式的调用数组元素前判断数组下标是否超过数组范围,如果超过,则判定为 hack 成功,返回 accepted 结果。
这就是说,如果你希望通过未定义行为进行 hack,只能对显式的调用数组元素的行为进行 hack。
样例程序
这是一份可以帮你理解你需要输出的内容的样例程序,但它不能给出正确的 hack 数据。直接提交此程序不会得分。
#include <iostream>
using namespace std;
int main() {
int taskId;
cin >> taskId;
if (taskId == 1) {
cout << "" <<endl;
} else if (taskId == 2) {
cout << "" << endl;
} else { // 这个 else 不会被执行
cout << "Stupid Fusu!" << endl;
}
}
如果你使用『提交答案』功能,请务必保证打开压缩包后能且仅能直接看到两个 .in
文件。这就是说,文件结构必须是:
ans.zip
|---1.in
|---2.in
两个文件不应该被额外的文件夹包含,即文件结构不能是:
ans.zip
|---ans(folder)
|---1.in
|---2.in
关于评测信息的说明
如果 hack 成功,对应测试点的信息为 accepted。如果返回其他信息,说明程序本身有误。
例如,如果返回 TLE,不代表成功的将对应程序 hack 至超时,而是表示数据生成器本身运行超时,测试点不得分。
特别的,返回 UKE 结果可能是数据不合法(有多余内容、缺少内容或数据范围不符合要求)。