一个微控制器芯片拥有 $B$ 位内置闪存。我们需要在其中存储并更新一个 $M$ 位的变量。闪存有一个限制:位可以单独从 0 变为 1,但只有通过擦除整个内存才能将位从 1 变为 0。内存的擦除次数有限,超过一定次数后芯片就会磨损并需要更换。因此,在擦除之前尽可能多地写入数值是理想的。
你的任务是设计一种高效的方法将数值存储到闪存中,以便始终能够检索到当前值。更准确地说,你的程序必须实现两种操作:
- 写入数值:此操作的输入是内存的当前状态和要写入的新数值,你需要输出内存的新状态。
- 读取数值:此操作的输入是经过若干次写入操作后的内存状态,你需要输出最后一次写入操作中写入的数值。
你的读取和写入操作除了从输入中读取内存的当前状态,以及写入操作在输出新状态前将某些位(可能为零)从 0 变为 1 之外,不能以任何其他方式交换信息。
交互
这是一个交互式任务。当你的程序启动时,输入的第一行包含整数 $T$,其中 $T = 0$ 表示你的程序将向内存写入数值,$T = 1$ 表示你的程序将从内存读取数值。第二行包含整数 $B$ 和 $M$。接下来的行描述了具体操作。
对于写入和读取,每个操作的第一行都包含整数 $C$,其中 $C = 0$ 表示不再有请求,你的程序应退出;$C = 1$ 表示你的程序应继续。
- 对于 $T = 0$ 且 $C = 1$,第二行包含两个以空格分隔的字符串:作为 $B$ 位序列的内存当前状态,以及作为 $M$ 位序列的要写入的新数值。如果你的程序可以通过仅将某些位从 0 变为 1 来将新数值写入内存,它应首先输出整数 1,并在下一行输出作为 $B$ 位序列的内存新状态。如果你的程序无法将新数值写入内存,则应输出整数 0。
- 对于 $T = 1$ 且 $C = 1$,第二行包含一个字符串:作为 $B$ 位序列的内存状态,你的程序应输出一个字符串:作为 $M$ 位序列的数值,即最后一次写入内存的数值。
样例
样例输入 1
0 6 2 1 111111 00 1 000000 11 0
样例输出 1
0 1 110000
样例输入 2
1 6 2 1 110000 1 110100 0
样例输出 2
11 01
说明
为了确保你的响应被正确传送到评测环境,你必须在每次响应后刷新输出缓冲区(同时注意输出总是以换行符结尾):
| 语言 | 命令 |
|---|---|
| C | fprintf(stdout, "1\n%s\n", s); fflush(stdout); |
| C++ | cout << 1 << "\n" << s << endl; |
| Java | System.out.println("1"); System.out.println(s); System.out.flush(); |
| Python | sys.stdout.write("1\n{0}\n".format(s)) sys.stdout.flush() |
测试
对于每个测试用例,将同时启动 4 个你的程序实例,其中 2 个用于写入,2 个用于读取。内存和 CPU 时间限制是所有这些实例的总和。任何在这些实例之间进行带外数据传输的蓄意尝试均被视为作弊,并将导致取消资格。
多个内存块(每个 $B$ 位)将被初始化为零。然后,写入和读取操作将以某种可行的顺序执行。
在写入操作期间,写入实例会获得其中一个块的当前状态以及要写入其中的数值。你可以假设要写入的数值是从范围 $0 \dots 2^M - 1$ 中均匀随机选择的,且与其他任何事物无关。如果你的程序能够写入该数值,则该块的状态将被设置为你的程序返回的状态。如果你的程序无法写入该数值,则该块将不再参与任何后续的写入操作。
在读取操作期间,读取实例会获得成功写入操作后的块状态。系统会检查你的程序返回的数值是否与写入操作期间应该写入的数值相同。读取操作将对每次成功写入操作的输出执行且仅执行一次。
子任务
在每个测试组中,你的程序得分与该组测试中平均写入的数值数量成正比。更准确地说,如果你的程序平均每个块能写入 $V$ 个数值,其得分将是该测试组分值的 $100 \cdot V/P \%$,其中 $P$ 的值如下给出。当你的程序在任何读取操作中返回错误数值时,整个组的得分将为零。对于任何其他失败,该测试中读取的数值数量将被计为零。
测试组满足以下条件:
- (5 分) $B = 16, M = 8, P = 4.062445024495069624056$
- (5 分) $B = 32, M = 8, P = 12.264904841300964834177$
- (5 分) $B = 32, M = 16, P = 4.129591513707784802006$
- (5 分) $B = 64, M = 8, P = 30.039277894268828900030$
- (5 分) $B = 64, M = 16, P = 12.953148094217360432715$
- (5 分) $B = 64, M = 32, P = 4.073559788233661501537$
- (5 分) $B = 128, M = 8, P = 69.777892228928747548775$
- (5 分) $B = 128, M = 16, P = 34.731791275143635240976$
- (5 分) $B = 128, M = 32, P = 13.950788987705638908663$
- (5 分) $B = 128, M = 64, P = 4.039918210604800133907$
- (5 分) $B = 256, M = 8, P = 174.468047086071038511453$
- (5 分) $B = 256, M = 16, P = 82.222614151404177334554$
- (5 分) $B = 256, M = 32, P = 37.629382269769206488916$
- (5 分) $B = 256, M = 64, P = 14.263462282054140577686$
- (5 分) $B = 256, M = 128, P = 4.015569093893943430859$
- (5 分) $B = 512, M = 16, P = 204.746242127410346170221$
- (5 分) $B = 512, M = 32, P = 91.778595148073111539847$
- (5 分) $B = 512, M = 64, P = 39.230279242145938712621$
- (5 分) $B = 512, M = 128, P = 15.000000002167672268601$
- (5 分) $B = 512, M = 256, P = 4.005423277111055468876$
此外,所有测试用例均满足 $N \cdot B \le 120\,000$,其中 $N$ 是你的程序可能被要求执行的最大写入操作次数。
在比赛期间,你的解决方案将在每个组的一小部分测试用例上进行评测。比赛结束后,你的最后一次提交以及在小规模测试集上得分最高的提交将在更大的测试集上进行评测,这两者中得分较高者将作为你该任务的最终得分。你在比赛期间在 CMS 中看到的得分并非你的最终得分。这样做的目的是为了提高得分的准确性。在比赛期间对完整测试集进行评测会过于缓慢。