【BZOJ4872】分手是祝愿
分手是祝愿
【题目大意】
有n 个灯,每个灯有两个状态亮和灭,我们用 1 来表示这个灯是亮的,用 0 表示这个灯是灭的,操作第 i 个开关时,所有编号为 i 的约数(包括 1 和 i)的灯的状态都会被改变,即从亮变成灭,或者是从灭变成亮。B 可以通过操作小于等于 k 个开关使所有灯都灭掉,那么他将不再随机,直接选择操作次数最小的操作方法(这个策略显然小于等于 k 步)操作这些开关。B 君想知道按照这个策略(也就是先随机操作,最后小于等于 k 步,使用操作次数最小的操作方法)的操作次数的期望。
【输入】
第一行两个整数 n, k。
接下来一行 n 个整数,每个整数是 0 或者 1,其中第 i 个整数表示第 i 个灯的初始情况。
1 ≤ n ≤ 100000, 0 ≤ k ≤ n;
【输出】
输出一行,为操作次数的期望乘以 n 的阶乘对 100003 取模之后的结果。
【解题思路】
讲课的ppt里只是简单的写了式子,都写了就不需要我讲了啊。对于一些我没在ppt里写的东西,可能讲得太快了没听清,更博造福人类啦。
- 做这dp类的题,要想真正得到提高,会更加关注怎么做和为什么这么做的问题。
- 首先明显的一个贪心,不管是拿多少分,这个贪心策略都是比较好想且需要用到的。
每操作一个灯对应的灯编号的约数也会改变状态,自然而然会想到:
从编号较大的往编号较小按开关。
这样肯定是最优的。
感性理解:
- 如果我们先按编号较小的开关,那么到头来还是要按编号较大的开关,因为编号较大而不构成其他数的因子,对于这些编号较大的开关,只有操作到这个编号本身才能改变状态。
- 无论何时按何开关,它能影响的开关数量以及编号都是一定的。这是由于它的编号而产生的性质,不随其状态的改变(明或暗)而改变。
- 操作编号较小的开关时不会影响到编号比它大的开关的状态。很显然编号较大的开关不构成编号较小的开关的约数,所以不会影响。
- 若较大编号的开关其编号是合数,不管什么时候按,它都会改变一些编号较小的开关的状态,这样先确定编号较大的开关的状态再依次确定编号较小的开关的状态,保证为最优解且具有唯一性。且每个开关至多被按一次,也就是说,最小次数一定少于开关总数。
通俗点讲就是,这个编号较大的开关你迟早都要按,产生的影响又是一定的,早点按确定状态,可以得出最优解所需按的开关集合。
理性认识
- 列出起始状态,将灯泡写作变量列出一个xor方程组 ,进行高斯消元,从而保证得出的是唯一解,这个高斯消元的过程是看别人的博客才知道的,可以证明出其具有唯一性。
- 我们还思考一个问题:
对于每个情况下,我们比最小次数多出来的操作次数是怎么来的。
- 我们是不是可以这样想啊:
对于每个开关,假如它是我们通向成功路(最小次数的方案)上的必经之路(最小次数的方案需要操作的开关集合里的开关),我们为什么花时会更多(次数多于最小次数),当然是因为你走了弯路(操作了不在最优集合里的开关),当然也不是不可以挽回的,因为你最终总是要到达终点(到达最终状态),你可以选择原路返回(再按一次按错的开关)或者绕更远的路绕到正道上(通过操作按错开关所影响的开关来挽回局面)。当然如果需要更快的返回正道,保证最小的步数当然是原路返回啊。
- 这题需要dp。我们为什么会想到dp?怎么dp?
我能说概率与期望题很多都是dp吗,否则就一脸不可做啊。
本题最值得考虑的问题应该是为什么要这样dp,这个dp的状态设置还是比较特别的,我ppt里一句带过,问问题的时候也没有人问。
可能大家都懂吧,这题直接秒了,我太弱了,那我再多想想也多讲讲。
在每个状态的情况下,按不同的灯可能导致的结果是不一样的。
为什么导致的结果不同???
本题结束游戏的最小步数是确定的,最小步数的方案中需要按哪些点也是确定的,这给这些点带来了一个新的性质:在或不在这个最小方案所要按的点集中。
- 如果按得开关编号在这个集合中,那么对于当前状态的结束游戏的最小次数发生了改变,减少了一。
- 如果不在这个集合中,那么结束当前游戏的最少次数也发生改变,你需要把它们按回来(间接或间接),来消除它们的影响。 当然在那样的情况下,最省次数的方案当然是直接把这个开关按回来,最小次数加一。
不懂的话看上面那个牵强的比喻。
- 为什么我们不用别的性质来dp???
用别的性质,比如:当前处理的点的编号???
设f[i]为操作到第i个点,剩下要操作?个点的期望步数。
但本题将最小次数及方案确定之后,你会发现相邻的点的编号是无需转移也没法转移的,因为最小方案需要操作点我们已经求出,这使得与之编号相邻这个性质不再具有意义,也无法转移状态。这样设置状态也没有阶段性,根本不能dp。
- 对于每个开关还有别的什么性质可以供我们dp 吗???
我不知道诶,如果你执着于还有其他设置状态的方式,please评论or QQ留言,然后我们一起讨论一波哈。
诶,说了这么多,我们到底怎么dp啊???
设f[i]为由最小次数为i到结束所需的期望步数。
f[i] = i/n*f[i-1] + (n-i)/n * f[i+1] + 1;
f[n] = f[n-1] + 1;
将所有点分成两个集合:最优情况需要按的开关集合,和其他开关集合。
我们有i/n的概率按到第一个集合里的开关,状态转移到了f[i-1];
另有(n-i)/n的概率按到第二个集合里的开关,状态转移成f[i+1];
我直接看神犇们的博客看的得有点懵逼啊。
写题的莫非智商超群或看了题解无缘无故谁会差个分诶。
回到这个式子本身,我们为什么会差分啊???
- 我们看一眼系数: f[i] = i/n*f[i-1] + (n-i)/n * f[i+1] + 1;
由于概率i/n+(n-i)/n = 1等于左边f[i]的系数,学过数列的孩子都有一种想要移项的冲动啊。(构数列,寻项标,对齐项标,构造新数列,balabalabala 这题跟这个好像没什么关系qwq)我们移个项 => i/n * g[i] = (n-i)/n*g[i+1] + 1;
g[i] = (n-i)/i * g[i+1] + n/i;
g[i] = ((n-i)*g[i+1] + n)/i;
- 另一种思考,同样基于这个式子:f[i] = i/n*f[i-1] + (n-i)/n * f[i+1] + 1;
f[i]:这个状态设置的是当前到结束的期望步数,最小步数为i
怎么搞一下把它简化啊:我们思考如何将上面那个式子中涉及到的相邻三项的递推简化成相邻两项,设置g[j]的状态为从最小步数j到最小步数j-1的期望步数,则有:
f[i] =sigama(1<=j<=i)g[j]。然后把这个式值带入之前的状态转移式化简一下,具体戳博客。同样可以得到关于g[i]的方程。