「SNOI2019」纸牌

#3098. 「SNOI2019」纸牌

Summarize

有一副纸牌,牌一共有 $n$ 种,每种有 $C$ 张。三张连号的牌或三张相同的牌可以组成一叠,如果一组拍可以分成若干叠,就称其为一组王牌。现已从牌堆摸了一些初始牌,需要再挑出一些牌组成一组王牌,求有多少种可能组成的王牌。

$1\le n \le 1e18,0\le C \le 1000$

Solution

麻将题这种套路我真不明白是怎么想出来的…… 总之能用就行了。

记 $f[i][a][b]$ 表示仅考虑前 $i$ 种牌,选了 $a$ 叠 $(i-1,i,i+1)$ 和 $b$ 叠 $(i,i+1,i+2)$ 的方案数。不难发现 $a$ 和 $b$ 均不超过 $3$,否则会有重复。

状态转移方程如下:

$$
f[i][b][c] = f[i - 1][a][b]\times (\lfloor\frac{C-s-\lceil d[i]-s\rceil}{3}\rfloor+1)
$$

将状态的后两维压至一维,即可矩阵大力转移。对于特殊点构造转移矩阵即可。

Note

动态规划转移前的可行性判断。

矩阵操作前(乘法、初始化单位矩阵)不要忘记清零!

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MOD = 998244353;

struct Matrix {
int m, n;
int g[9][9];
};

Matrix mul(Matrix a, Matrix b) {
Matrix c;
c.m = a.m, c.n = b.n;
for (int i = 0; i < c.m; ++i)
for (int j = 0; j < c.n; ++j) {
c.g[i][j] = 0;
for (int k = 0; k < a.n; ++k)
c.g[i][j] = (c.g[i][j] + 1ll * a.g[i][k] * b.g[k][j] % MOD) % MOD;
}

return c;
}

ll n;
int C, x;

void mk(Matrix& a) {
a.m = a.n = 9;
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j)
a.g[i][j] = i == j ? 1 : 0;
}

Matrix qpow(Matrix a, ll n) {
Matrix ret;
mk(ret);
while (n) {
if (n & 1) ret = mul(ret, a);
a = mul(a, a);
n >>= 1;
}
return ret;
}

int main() {
scanf("%lld%d", &n, &C);
Matrix X;
X.m = 9, X.n = 1;
X.g[0][0] = 1;
for (int i = 1; i < 9; ++i)
X.g[i][0] = 0;
Matrix F;
F.m = F.n = 9;
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j)
F.g[i][j] = 0;
for (int b = 0; b <= 2; ++b)
for (int c = 0; c <= 2; ++c)
for (int a = 0; a <= 2; ++a) {
int s = a + b + c;
if (C < s) continue;
F.g[a * 3 + b][b * 3 + c] = (C - s) / 3 + 1;
}
scanf("%d", &x);
ll cur = 0;
for (int t = 1; t <= x; ++t) {
ll p;
int d;
scanf("%lld%d", &p, &d);
X = mul(qpow(F, p - cur - 1), X);
Matrix G;
G.m = G.n = 9;
for (int i = 0; i < 9; ++i)
for (int j = 0; j < 9; ++j)
G.g[i][j] = 0;
for (int b = 0; b <= 2; ++b)
for (int c = 0; c <= 2; ++c)
for (int a = 0; a <= 2; ++a) {
int s = a + b + c;
if (C < s) continue;
int cer = 0;
if (d <= s) cer = s;
else cer = s + 3 * ((d - s + 2) / 3);
if (C < cer) continue;
G.g[a * 3 + b][b * 3 + c] = (C - cer) / 3 + 1;
}
X = mul(G, X);
cur = p;
}
X = mul(qpow(F, n - cur), X);
printf("%d", X.g[0][0]);
return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×