昨晚搓了一局雀魂,牌运还不错;今天和同学聊天的时候提到了,然后顺便翻出了这道题和隔壁 GXOI/GZOI 那道麻将题。
然后顺便研究了一下这道题,其实也不是特别难写,不过想出来应该也很难。
首先我们考虑如何判定一堆牌的子集能否和牌。
注意到我们并不关心牌之间的顺序,故可以将这堆牌表示为「第 i 种牌有 ai 种」的形式。
不难想到 DP,设 fi,0/1,j,k 表示考虑到第 i 种牌,是否预留过雀头,预留了 j 组 (i−1,i) 和 k 张 i 的情况下能凑出的最多的面子数。
并且因为 j≥3 或 k≥3 都可以算为面子,所以 j<3,k<3。
如果我们要多次判定但只能做一次 DP 呢?
可以考虑将这个 DP 改成自动机。具体地,考虑把 f 数组删去第一维得到的数组作为一个自动机上的结点,DP 转移对应自动机上的转移。
在构造的时候,不考虑 i 的影响,因为最后无论如何都一定会得到和牌的状态。而得到和牌状态时则不能再改变状态,否则便会无限进行下去。
这样可以把相同结点合并,经实测,最多会有 2092 个结点。
判定的时候,只需要在自动机上跑边即可。
那么此题显然可以考虑在自动机上再设计一个 DP,达到 DP 套 DP 的效果。
考虑设 gi,j,k 表示考虑到第 i 种牌,已经摸了 j 张牌,走到自动机上的 k 号结点有多少种方案。
可以容易地从这个 DP 得到 g′i 表示摸 i 张牌都不和牌的方案数,由此便可知答案为 1+1(4n−13)!4n−13∑i=1g′ii!(4n−13−i)!
因为如果某种排列下摸 i+1 张牌才能和牌而 i 张牌则不行,那么对 i−1,i−2,… 也会有贡献,总共就是刚好 i 次贡献。 并且确定了排列的前 i 张牌是哪些牌,顺序无所谓,后面的牌顺序也无所谓,所以要乘阶乘。
而题目问的是第一张能和牌的牌,所以要加一。
所以好像还没说 g 的转移?
也蛮简单的吧,注意枚举取多少第 i 种牌时要乘上一个组合数。
代码:
1 |
|