IPSC 2011 Problem I – Inverting bits
题目链接
IPSC 2011 Problem I – Inverting bits
题目大意
你有 \(26\) 个变量 \(A-Z\),每个变量初始为 \(0\),可以存储一个 \(8\) 位无符号整数,你可以对它们做「 赋值、按位与、按位或、取反、左移、右移、读入、输出」 \(8\) 种操作,运算中可以使用数字常量。
现在你要读入 \(n\) 个 \(0/1\) 值,将它们 flip 之后按序输出,并且不得使用超过 \(k\) 次取反操作。
Easy Subproblem: \(n=7,k=1\)
Hard Subproblem: \(n=19,k=2\)
思路
Easy Subproblem 把 \(7\) 个数字塞到一个变量的前 \(7\) 个二进制位上取反后拆回来输出即可。
Hard Subproblem 相对难一些,有一个直观但延展性差的做法和一个抽象但潜力巨大的做法。
\(n=19\) 很有迷惑性,两个做法都是把它们塞到 \(3\) 个 \(8\) 位整数中然后取反的。
法一
把每个数字看作一个集合,由二进制上为 \(1\) 的位置组成,于是「按位与、按位或和取反」就变成了「取交集、取并集、和全局取补集」的集合操作,把维恩图画出来:
(好像重名了,emmm反正全集用不上不改了)
我们设 \(E\) 为中间三叶草形状的区域:
简洁起见用 \(AB\) 表示 \(A\cap B\),\(A+B\) 表示 \(A\cup B\),\(\overline A\) 表示 \(\complement_U A\),则 \(E=AB+BC+CA\) .
对 \(E\) 取反,可以发现可以算出 \(X=\overline EA,Y=\overline EB,Z=\overline EC\),利用这个,我们再构造一个图形 \(F\) :
\(F=X+Y+Z+ABC\),而 \(\overline F\) 很有用,通过它可以得到 \(U=\overline FAB,V=\overline FBC,W=\overline FCA\),然后我们所需要的信息基本就全了,可以发现原来要求的反即两个扇形(\(X/Y/Z\))和一个三叶草的叶子(\(U/V/W\))以及外面的补集组成,而外面的补集即 \(\overline F\overline E\),所以就做完了:
\(\overline A=\overline E \overline F+Y+Z+W\),\(\overline B=\overline E \overline F+X+Z+V\),\(\overline C=\overline E \overline F+X+U+Y\) 。
这个做法基本上属于利用了数据小的特点,瞎试出来的,拓展到一般情况很困难,毕竟大小为 \(4\) 的维恩图就已经比较难画了,因此一般情况需要另一个做法。
法二
题解思路,由于把 \(0/1\) 值塞到 \(8\) 位整数里是纯技术问题,下面的 \(n\) 是题面中的 \(\lceil\frac{n}{8}\rceil\) 。
我们的操作中不包含条件语句,所以需要一种全局性的方法去解决所有情况,注意到二进制的每一位之间是互不干扰的,所以可以分开来考虑,要求我们的做法对于任意一位上的 \(0/1\) 排列都能进行翻转,问题便转化为翻转 \(n\) 个 \(0/1\) 值。
注意到问题的本质就是当一位是 \(1\) 时,将它变为 \(0\),反之变为 \(1\),所以操作需要区分 \(0\) 和 \(1\),那么可以这样区分:假设对于 \(i\in[0,n]\),我们知道 \(ex(i)\) 的真假情况,即 \(n\) 个值中是否恰好有 \(i\) 个 \(1\),,那么当前值 \(a_i\) 翻转后的值可以表示为:
其中 \(And(S)\) 和 \(Or(S)\) 分别表示集合 \(S\) 中所有元素的「与和」跟「或和」,若 \(a_i\) 在那若干个 \(1\) 中,则式子为 \(0\),否则为 \(1\) 。于是现在的问题便转化成了如何在 \(2\) 次取反操作内,得到 \(ex(0)\) 到 \(ex(3)\),注意到 \(ex(i)=[one(all)\geq i]\&[one(all)\leq i]\),于是引入 \(lst(i)=[one(all)\geq i],mst(i)=[one(all)\leq i]\),容易发现:
经过一定的思考便可以得到原题的解法了:
第 \(5\) 步的手法很奇妙,我们把它拓展一下,开始我们先用一次取反得到 \(mst(mid)\),可以发现 \([0,mid]\) 和 \([mid+1,n]\) 变成了和原来相同的子问题,注意原来的 \(mst(n)\) 没啥用,它必然为 \(1\),但是在子问题中右端点的 \(mst\) 很有用,并且在求 \(mst(mid)\) 的时候已经得到了。
使用全局分治的思路,在求下一层分治的 \(mst(mid)\) 的时候,我们直接求出所有 \(mid\) 位置 \(mst\) 的或,可以发现即 \(\sim And(\{lst(i+1)|i\in{\text{下一层的mid}}\})\),这个值再与上 \(mst(r)\) 即为 \(mst(mid)\) 了,这样递归总共有 \(\lceil \log_2 (n+1)\rceil\) 层,每层只需要使用一次取反,所以使用这种方法,取反 \(n\) 个数字只需要至多 \(\lceil \log_2 (n+1)\rceil\) 次取反操作即可。
不大会证这是否是下界,但应该也算是比较优秀的做法了吧。