TIOJ 1705 山洞 題解

本文最後更新於:2022年12月8日 下午

TIOJ 1705

題序

見原題

解法

不難看出這是一題矩陣快速冪的題目。

由於轉移點會有兩排,因此狀態數共 $2^6 \times 2^6 = 2^{12}$ 種,用這樣的狀態數下去做矩陣乘法的複雜度就會是 $\mathcal{O}(2^{36})$,顯然不符合需求。

觀察到題目有給一個限制:每排都需要鑽兩個洞,因此每一排的狀態數就減少到了 $C^6_2$ 種,再扣掉不合法的狀態,就剩下 $69$ 種。

接著我們可以枚舉狀態,利用合法或不合法來建構出轉移矩陣 $T$,最後做 $T^{n-2}$ 就可以得到答案了。

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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#include <bits/stdc++.h>
#define Weakoying ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define int long long
#define pii pair<int, int>
#define vi vector<int>
#define vii vector<pair<int, int>>
#define pqueue priority_queue
#define pb push_back
#define F first
#define S second
#define max(a, b) (a > b ? a : b)
#define min(a, b) (a < b ? a : b)
#define cmax(a, b) a = (a > b ? a : b)
#define cmin(a, b) a = (a < b ? a : b)
#define put(x) cout << x << endl;
#define DB(x) cerr << #x << " " << x << endl
#define all(v) v.begin(), v.end()
#define stop system("pause");
#define MEM(x, n) memset(x, n, sizeof(x));
#define lowbit(x) x &(-x)
#define SZ(x) ((int)x.size())
#if !LOCAL
#define endl "\n"
#pragma optimize("Ofast", "unroll-all-loops")
#endif
const int INF = 0x3f3f3f3f3f3f3f3f;
const int P = 10000;

using namespace std;
/******************************************************************************/
#define MAXN 200005
#define MAXM 1000005
int n, m;
vector<int> masks;

struct matrix {
vector<vector<int>> cont;

matrix(const int _n, const int _m) {
for (int i = 0; i < _n; i++)
cont.emplace_back(vector<int>(_m, 0));

}

matrix(const int _n) {
for (int i = 0; i < _n; i++)
cont.emplace_back(vector<int>(_n, 0));
for (int i = 0; i < _n; i++)
cont[i][i] = 1;
}

matrix operator *(const matrix &_a) {
matrix res(SZ(cont), SZ(_a.cont[0]));

for (int i = 0; i < SZ(cont); i++) {
for (int j = 0; j < SZ(_a.cont[0]); j++) {
res.cont[i][j] = 0;
for (int k = 0; k < SZ(_a.cont); k++) {
res.cont[i][j] += cont[i][k] * _a.cont[k][j];
}
res.cont[i][j] %= P;
}
}

return res;
}

void operator %=(const int P) {
for (auto &col: cont)
for (auto &it: col)
it %= P;
}


int operator () (){
int res = 0;
for (auto &col: cont)
for (auto &it: col) {
res += it;
}
res %= P;
return res;
}
};

bool check(int i) {
return (i >= 0 && i < 6);
}

matrix make_T() {
matrix res(SZ(masks), SZ(masks));

for (int i = 0; i < SZ(masks); i++) {
int mask1 = (masks[i] >> 6); // i - 1
int mask2 = masks[i] & 0b111111; // i
for (int j = 0; j < SZ(masks); j++) {
int mask3 = (masks[j] >> 6); // i - 2
int mask4 = masks[j] & 0b111111; // i - 1

res.cont[i][j] = (mask4 == mask1);


for (int x = 0; x < 6; x++) if (mask3 & (1 << x)) {
for (int y = 0; y < 6; y++) if (mask4 & (1 << y)) {
for (int z = 0; z < 6; z++) if (mask2 & (1 << z)) {
if (abs(x - z) == 1 || abs(x - y) == 2 || abs(y - z) == 2) {
res.cont[i][j] = 0;
goto fin;
}
}
}
}
fin:;
}
}
return res;
}

template<typename T>
T fpow(T a, int b, const int P) {
T res(SZ(masks));

while (b) {
if (b % 2) {
res = res * a;
// res %= P;
}
a = a * a;
// a %= P;
b >>= 1;
}
return res;
}

void sol() {
cin >> n;

if (n == 1) {
int ans = 0;
for (int mask = 0; mask < (1 << 6); mask++)
ans += __builtin_popcount(mask) == 2;
cout << ans << endl;
return;
}

for (int mask1 = 0; mask1 < (1 << 6); mask1++) {
if (__builtin_popcount(mask1) != 2)
continue;
for (int mask2 = 0; mask2 < (1 << 6); mask2++) {
if (__builtin_popcount(mask2) != 2)
continue;

bool legal = true;

for (int x = 0; x < 6; x++) if (mask1 & (1 << x)) {
for (int y = 0; y < 6; y++) if (mask2 & (1 << y)) {
if (abs(x - y) == 2)
legal = false;
}
}

if (legal) {
masks.emplace_back((mask1 << 6) + mask2);
}

}
}


auto T = make_T();
matrix A(SZ(masks), 1);
for (int i = 0; i < SZ(masks); i++)
A.cont[i][0] = 1;

T = fpow(T, n - 2, 10000);
A = T * A;

if (n >= 6) {
cout << string(4 - to_string(A()).size(), '0');
}

cout << A() % 10000 << endl;
}

signed main() {
Weakoying;
int t = 1;
//while (cin >> t)
{
while (t--) {
sol();
}
}

return 0;
}

TIOJ 1705 山洞 題解
http://koyingtw.github.io/TIOJ-1705/
作者
Koying
發布於
2022年12月8日
許可協議