以下是本人的一些脑洞,不喜勿喷。
UOJ #289(【WC2017】排序)是一道提交答案题,然而在UOJ上却以传统题的形式出现。这看起来十分不和谐,违背了UOJ(Universal Online Judge)的“Universal”特点。况且改成传统题以后做题也十分不方便,比如本人第1个测试点写的是 $O(n^2K)$ 的程序,尽管可以跑出来但无法在 $2\texttt{s}$ 内跑出来,于是虽然考场上可以通过,却无法在UOJ上提交通过。
之前本人联系过管理员jiry_2将这题改为提交答案题,得到的答复是:不太行,上传的文件太大了。然而仅仅因为输出文件太大就可以让这题“变味”吗?显然不行!
于是,我有以下几种想法:
1、压缩提交文件大小
这题一共 $\sum T=100$ 组数据,每组数据输出的比较器可以达到 $num=10^6$ 个,这意味着需要输出 $10^8$ 个三元组 $(u,v,t)$。
由于 $1\le u < v\le n\le 10^5$,$1\le t\le 150$,所以本质不同的比较器约有 $7.5\times 10^{11}$ 种,而 $2^{40} > 10^{12}$,意味着存储一个比较器需要用大约 $40$ 个bit。如果要存 $10^8$ 个这样的三元组,那么就需要 $4\times 10^9$ 个bit,约 $500\texttt{MB}$。
这个数据文件实在太大了,能不能压缩?
注意到 $t\le 150$,可以对每一个 $t$ 存这一层所有的 $(u,v)$ 二元组。另外,这些二元组是顺序无关的,可以将所有的二元组按 $u$ 排序,然后存相邻两个 $u$ 的距离。考虑一种比较坏的情况:每层都有 $10^6/150$ 个二元组,相邻的 $u$ 距离相等,这个距离约是 $15$。考虑用这种方法表示一个数:先输出 $t$ 个 $1$,然后输出一个 $0$,最后输出一个 $4t$ 位整数。如果按平均 $t=2$ 来估计,由于同时也要存所有的 $v$,那么输出一个二元组平均需要 $11+17=28$ 个bit,这样总共文件大小就是 $2.8\times 10^9$ 个bit,约 $350\texttt{MB}$。
遗憾的是输出文件似乎仍然很大。并不知道能否用一些数据压缩算法来进一步减小数据大小。不过看起来不太可行……(也许jiry_2有更好的办法?)
2、在本机上进行评测
那么,是不是可以参考现场的评测方法呢?
考试现场是在本机上测试的。实际上,选手在做题的时候就已经知道了得分。
因此可以用一种方法:在本机上测试完之后,将得分提交到UOJ。不过直接提交得分是有问题的,因为选手可以直接改这个得分,如果选手并没有通过这题,但通过直接修改得分而制造了满分的假象,就很不公平了。
我们设计一个程序 $P$,$P$ 接受用户的输入文件,计算出各测试点的得分情况 $S$,然后输出 $O=f(S)$,其中 $f$ 是一个加密函数。提交到UOJ之后,将提交的文件 $O$ 进行解密,得到 $S=f^{-1}(O)$。如果加密函数 $f$ 设计得足够好,在不知道 $f$ 函数的情况下,提交文件 $O$,解密出的 $S=f^{-1}(O)$ 相当于一个随机数,能够通过测试的可能性几乎为 $0$。
这样得到的结果可能是所有人提交的文件都相同,就像UOJ #200一样。其实这并没有什么关系……
Q:如果选手用其他人的提交文件然后直接AC了,怎么办?
A:可以将题目设置为AC以后才能查看代码,如UOJ #259。另外即使不这么做也没关系,可以使加密文件大小超过 $500\texttt{B}$,由于UOJ只能查看提交的前 $500\texttt{B}$ 文件,使用其他人的提交文件也不管用了。
另外此做法可能还是有一些漏洞,欢迎提出。
UPD:目前还有一个待解决的问题:如何防止反编译程序破解加密器。这个问题本人暂时还没想到如何解决,欢迎提供建议。