UOJ Logo WrongAnswer的博客

博客

一些可能可以使UOJ #289改成真正的提交答案题的方法

2017-03-01 22:03:46 By WrongAnswer

以下是本人的一些脑洞,不喜勿喷。

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:目前还有一个待解决的问题:如何防止反编译程序破解加密器。这个问题本人暂时还没想到如何解决,欢迎提供建议。

评论

matthew99
前排
matthew99
第二种方法合理而又新颖,以后可能真会有这种类型的题
AntiLeaf
要用第二种方法的话,可能还需要考虑UOJ可以看其他人的提交文件这一点,如果直接把别人的提交结果copy过来,选手岂不是又随便AC了。
Sengxian
第二种方法,可以让 O = f(username + S),服务器解密的时候验证一下用户名即可。 这样即使知道了别人的提交结果,也不能直接拿过来 AC。
ruanxingzhi
大赞第二种方法。 推荐把分数和用户名一起hash(相当于加盐),这样即使提交别人的hash码,也无法AC。 例如:hash(userName , score) = md5(SHA1(userName)+md5(score)) 服务器写一个spj,从0到100枚举每一个score,结合用户名算出hash,即可判定用户得了多少分。 这个在UOJ上可能比较难实现。我们不能方便地在spj里面知道用户名。
mcfxmcfx
然而并不能防止反编译程序 P啊

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。