#P16268. [NOISG 2019] Shuffle

[NOISG 2019] Shuffle

题目背景

Lim Li 非常喜欢动画,最近她从 Amazon 购买了一整季《干物妹!小埋》。这一季共有 NN 集,对应 NN 张 CD。CD 的标签编号为 1,2,,N1,2,\ldots,N,剧集编号也为 1,2,,N1,2,\ldots,N

然而,由于生产故障,CD 标签编号与其中实际存放的剧集编号并不对应。Amazon 告诉她:所有剧集都在这套 CD 中,没有缺失,只是顺序被打乱了。

为了避免剧透,Lim Li 不愿意自己播放 CD 来确认每张 CD 对应哪一集。于是她决定请朋友 Rar the Cat 帮忙。

一次询问的过程如下:

  1. Lim Li 将 NN 张 CD 分成 BB 个盒子,每个盒子中恰好有 KK 张 CD,满足 N=B×KN=B\times K
  2. 这些盒子会寄给 Rar。在运输过程中,每个盒子内部 CD 的顺序可能被打乱;除第 3 个子任务外,盒子之间的顺序也可能被打乱。
  3. Rar 收到盒子后,会播放 CD,并记录每个盒子中 CD 对应的剧集编号。
  4. Rar 将每个盒子的剧集编号分别写在 BB 张纸上寄回给 Lim Li。每张纸上有 KK 个整数,表示某个盒子中 CD 对应的剧集编号。
  5. 所有 CD 会被寄回给 Lim Li。

这个过程最多可以进行 QQ 次。请帮助 Lim Li 确定每个 CD 标签对应的实际剧集编号,并尽量减少询问次数。

题目类型

本题为 函数式交互 / Grader 题

选手程序不要读入标准输入,也不要向标准输出输出内容。你只需要包含头文件 shuffle.h,并实现指定函数 solve

评测时,系统会自动提供评测主程序 grader.cpp,并将其与你提交的程序一起编译运行。

需要实现的函数

你需要实现:

#include "shuffle.h"

std::vector<int> solve(int N, int B, int K, int Q, int ST);

该函数会被调用恰好一次。

参数含义如下:

  • NN:CD 数量,同时也是剧集数量;
  • BB:每次分组的盒子数量;
  • KK:每个盒子中的 CD 数量;
  • QQ:最多允许调用 shuffle 的次数;
  • STST:当前测试点所属的子任务编号。

函数应返回一个长度为 NN 的数组 ans,其中 ans[i-1] 表示标签编号为 ii 的 CD 中实际存放的剧集编号。

可以调用的函数

你可以调用评测器提供的函数:

std::vector<std::vector<int>> shuffle(std::vector<std::vector<int>> boxes);

参数 boxes 表示 Lim Li 当前的分盒方案。它必须满足:

  • boxes 恰好包含 BB 个数组;
  • 每个数组恰好包含 KK 个整数;
  • 所有整数合起来必须恰好是 1,2,,N1,2,\ldots,N 的一个排列。

也就是说,boxes 的每一行表示一个盒子中放入的 CD 标签编号。

函数 shuffle 会返回一个 B×KB\times K 的二维数组,表示 Rar 收到盒子后记录的剧集编号。返回结果中:

  • 每一行对应某一个盒子中的 KK 个剧集编号;
  • 每行内部顺序可能被打乱;
  • 除第 33 个子任务外,返回结果中各行的顺序也可能被打乱。

注意:传给 shuffleboxes 本身不会被修改。

如果调用 shuffle 的次数超过 QQ,或者传入参数不合法,程序会被判为错误。

样例交互说明

假设:

N=6,B=3,K=2,Q=100,ST=2,N=6,\quad B=3,\quad K=2,\quad Q=100,\quad ST=2,

并且真实对应关系为:

[3,1,4,5,2,6],[3,1,4,5,2,6],

即标签 11 对应剧集 33,标签 22 对应剧集 11,依此类推。

此时评测器会调用:

solve(6, 3, 2, 100, 2)

一次可能的交互如下:

shuffle([[1, 2], [3, 4], [5, 6]]) = [[6, 2], [5, 4], [3, 1]]

这表示 Lim Li 分出的三个盒子分别装有标签 [1,2][1,2][3,4][3,4][5,6][5,6]。运输后盒子顺序和盒内顺序可能改变,Rar 记录到的三个盒子中的剧集编号为 [6,2][6,2][5,4][5,4][3,1][3,1]

之后可能继续调用:

shuffle([[2, 6], [3, 1], [5, 4]]) = [[6, 1], [2, 5], [4, 3]]
shuffle([[6, 5], [4, 2], [3, 1]]) = [[5, 1], [3, 4], [2, 6]]

若你的程序最终确定真实对应关系为:

[3, 1, 4, 5, 2, 6]

则应从 solve 中返回这个数组。

约束条件

所有测试点满足:

B,K2,B,K\ge 2, N6,N\ge 6, N=B×K.N=B\times K.

单个测试点的时间限制为 1.01.0 秒。

子任务

子任务 分值 附加限制
1 2 N=6,B=2,K=3,Q=100N=6, B=2, K=3, Q=100
2 3 N=6,B=3,K=2,Q=100N=6, B=3, K=2, Q=100
3 12 N1000,Q=12N\le 1000, Q=12,盒子之间的顺序不会被打乱
4 16 N1000,K=2,Q=4N\le 1000, K=2, Q=4
5 15 N1000,B=2,Q=12N\le 1000, B=2, Q=12
6 52 N1000,Q=2000,B,K>2N\le 1000, Q=2000, B,K>2,详见下方计分方式

第 6 个子任务的计分方式

第 6 个子任务为特殊计分子任务。设你的程序在任意一个第 6 子任务测试点中使用的最大询问次数为 qq

  • q>2000q>2000,得 00 分;
  • 500<q2000500<q\le 2000,得 88 分;
  • 50<q50050<q\le 500,得 1717 分;
  • 9<q509<q\le 50,得
22+30(50q41)222+30\left(\frac{50-q}{41}\right)^2

分;

  • q9q\le 9,得 5252 分。

本地测试输入格式

下发的本地评测器使用如下输入格式:

第一行包含五个整数:

N,B,K,Q,ST.N,B,K,Q,ST.

第二行包含 NN 个整数:

E1,E2,,EN,E_1,E_2,\ldots,E_N,

其中 EiE_i 表示标签编号为 ii 的 CD 中实际存放的剧集编号。

正式评测时,选手程序不需要也不应该自行读取这些输入。

提交说明

选手提交文件中应包含:

#include "shuffle.h"

并实现:

std::vector<int> solve(int N, int B, int K, int Q, int ST) {
    // 在这里实现你的算法
}

不要在提交文件中编写 main 函数。评测系统会自动提供 main 函数和 shuffle 函数。

下发文件与本地测试说明

本题下发文件中包含:

shuffle.h        // 需要包含的头文件
shuffle.cpp      // 选手代码模板
compile_cpp.sh   // 本地编译脚本
grader.cpp      // 本地评测器,实际文件名为 grader.cpp
run_cpp.sh       // 本地运行脚本
sample.1.in      // 样例输入

本地测试时,可以将自己的代码写入或替换 shuffle.cpp,然后在 Linux/macOS 环境下运行:

chmod +x compile_cpp.sh run_cpp.sh
./compile_cpp.sh
./run_cpp.sh < sample.1.in

如果程序正确,评测器会输出类似:

Correct. Used 4 out of 100 queries.

正式提交时,只需要提交实现了 solve 的源代码;不需要提交 grader.cppcompile_cpp.shrun_cpp.sh

@下发文件