# mahjong
### 题目描述
ame 是一个可爱的女孩子,她喜欢打麻将。
ame 找来了一副特殊的麻将,为了防止你不知道麻将的相关规则,在此给出一些定义:
* 这副麻将共含有 $m$ 种花色,有 $n$ 种不同的数字,大小分别为 $1$ 到 $n$。
* 一共有 $n\times m$ 种牌,每种牌为一个两两不同的二元组 $(x,y)$,表示这种牌数字为 $x$,花色为 $y$,每种牌都有 $4$ 张。其中 $1\leq x\leq n,1\leq y\leq m$。
* 一组面子可以为一组刻子或一组顺子。
* 定义一组顺子是三张花色相同、数字连续的牌。
* 定义一组刻子是三张花色和数字组合完全相同的牌。
* 定义一组对子是两张花色和数字组合完全相同的牌。
定义一个麻将牌可重集合 $S$ 是胡的当且仅当:
* 大小为 $3k+2$,其中 $k$ 为给定的数值。
* 能够被划分为 $k$ 组面子和一组对子。
游戏开始时,ame 从所有牌当中取出了一个大小为 $3k+2$ 的麻将牌集合 $S$,她想知道对于所有不同的 $S$,其中能胡的有多少个。
定义两个麻将牌集合 $S$ 和 $T$ 不同当且仅当存在一种牌,在 $S$ 中的出现次数和在 $T$ 中的出现次数不同。
对于所有数据,$4\leq n \leq 10^9$,$1\leq m\leq 10^9$,$1\leq k\leq 100$。
### 输入格式
输入共 $1$ 行 $3$ 个正整数 $n,m,k$。
### 输出格式
输出共 $1$ 行 $1$ 个整数,表示对 $998244353$ 取模之后的答案。
### 数据范围
对于所有数据,$4\leq n \leq 10^9$,$1\leq m\leq 10^9$,$1\leq k\leq 100$。
| 数据编号 | $n \leq$ | $m$ | $k\leq$ | 时间限制 |
| :-: | :-: | :-: | :-: | :-: |
| 1 | $9$ | $= 1$ | $4$ | 4s |
| 2,3,4,5,6 | $100$ | $\leq 10^9$ | $100$ | 4s |
| 7,8,9,10,11 | $10^9$ | $=n$ | $100$ | 4s |
| 12 | $10^9$ | $\leq 10^9$ | $1$ | 4s |
| 13 | $10^9$ | $\leq 10^9$ | $2$ | 4s |
| 14 | $10^9$ | $\leq 10^9$ | $3$ | 4s |
| 15 | $10^9$ | $\leq 10^9$ | $4$ | 4s |
| 16,17,18,19,20 | $10^9$ | $\leq 10^9$ | $30$ | 4s |
| 21,22,23,24,25 | $10^9$ | $\leq 10^9$ | $100$ | 4s |
### 题解
**算法一**
暴力枚举麻将牌集合,然后贪心判断即可。
**算法二**
可以手玩方案数!
**算法三**
显然能够发现如果设 $f_{i,0/1}$ 表示合法麻将牌集合 $S$ 的大小为 $i$,且这些牌的花色相同,其中包不包含一个对子的数量,则能用组合数 $O\left(k^3\right)$ 求出答案。
具体而言,设 $g_{i,j}$ 表示共有 $i$ 种花色,集合大小为 $j$ 的方案数。那么可以用 $f$ 背包求出 $g$ ,然后再用 $g$ 组合数即可。
考虑如何求出 $f_{i,0/1}$,我们能够设一个 dp,然后从 $1$ 到 $n$ 依次考虑每个大小的牌加多少个,现在
需要判断的是加进去之后是否合法。
转化为一个这样的命题:给定一个集合,能否胡牌。有一个简单的 dp,设 $dp_{i,s_1,s_2,0/1}=[0,1]$ 表示当前考虑第 $i$ 张牌,从 $i-2$ 开始的顺子有 $s_1$ 个,从 $i-1$ 开始的顺子有 $s_2$ 个,有没有对子的情况是否合法。转移显然。
发现 $(s_1,s_2,0/1)$ 的总状态数量为 $18$,也就是说,用一个大小为 $18$ 的数组就能包括当前麻将牌集合能表示出的所有状态。
那么我们设 $dp_{i,j,S}$ 为,当前加入到第 $i$ 张牌,一共加入了 $j$ 张牌,能表示状态的集合为 $S$ 的方案数。转移显然。
不过 $2^{18}$ 种方案显然是非常大的,我们能从初始状态预处理出能到达的所有状态,经打表一共只有 $68$ 种。
$68$ 种的预处理可以参考 ZJOI2019 麻将的 bfs 写法,接着套用上面那个 dp 就行了。
时间复杂度:$O(68nk)$。
**算法四**
经实践,当 $k$ 固定时,能用 BM 算法暴力求出递推式,可以通过 $k=1,2,3,4$ 的部分分。
**算法五**
$k$ 较小的情况下,发现有大量的数字都不会被加入。
那么我们只需考虑若干连续的数字组成的方案数,然后再合并即可。
考虑每一个合法的仅有一种花色且集合大小为 $s$ 的牌,显然 $s=3n+2(n\in \mathbb N)$,且能够将其分为若干段,这若干段每段的数字连续(可重复),但是不同段之间的数字不连续,显然对每段的牌内部也应是合法的。那么设 $f_{i}$ 表示分出来的某一段长度为 $i$ 的方案数,这个可以用**算法三**中的方法计算,然后再令 $g_{i,j}$ 表示同一种花色共有 $i$ 段,集合大小为 $j$ 的方案数,这个可以用求得的 $f$ 背包得到。
注意这个 $g_{i,j}$ 不考虑具体的牌的大小,但是考虑不同段之间大小的相对顺序。
* 例如对于牌组 $\{2,3,4,6,7,8,9,9\}$ 在 $g_{i,j}$ 中与 $\{1,2,3,6,7,8,9,9\}$ 相同,但是与 $\{2,3,4,5,5,7,8,9\}$ 不相同。
求得 $g_{i,j}$ 后可以用组合数求出考虑具体牌的大小后的方案数,最后用求得的 $g_{i,j}$ 再组合数背包求出考虑多个花色的答案。
时间复杂度:$O\left(68\times 9\times k^3\right)$。
**算法六**
我们发现被卡常了,想一下怎么优化常数。
对于每个状态 $S$,当前合法的麻将牌集合大小模 $3$ 的余数一定相等。
发现常数主要集中在求 $f$ 的地方,然后发现对预处理的 $68$ 种状态,满足某个状态的牌,一定满足数量大小模 $3$ 的余数大小一定。于是可以用这个性质优化常数,再优化一下循环的上下界即可。
使用类似思想可以优化 $9$ 倍常数。
时间复杂度:$O\left(68k^3\right)$。
#### 参考代码
```c++{.line-numbers}
#include
#include
#include
#include
#include
#include
#include
#include
#include