有 N 種拼圖塊,第 i 種右側可接的塊集合給定。求長度 ≤ M(≤ 105)的不同拼接模式數,模 2015。空序列也算一種。
引入增廣矩陣 B = [A, 0; 1…1, 1],則 BM 的第一行和即為答案。
struct Matrix {
int a[55][55];
Matrix operator*(const Matrix &o) const {
Matrix r; memset(r.a, 0, sizeof(r.a));
for (int i = 0; i < n; ++i)
for (int k = 0; k < n; ++k) if (a[i][k])
for (int j = 0; j < n; ++j)
r.a[i][j] = (r.a[i][j] + a[i][k] * o.a[k][j]) % MOD;
return r;
}
};
Matrix qpow(Matrix base, long long exp) {
Matrix res; // 單位矩陣
for (int i = 0; i < n; ++i) res.a[i][i] = 1;
while (exp) {
if (exp & 1) res = res * base;
base = base * base;
exp >>= 1;
}
return res;
}
#include <bits/stdc++.h>
using namespace std;
const int MOD = 2015, MAXN = 55;
int n; long long m;
struct Matrix {
int a[MAXN][MAXN];
Matrix() { memset(a, 0, sizeof(a)); }
void identity() { for (int i = 0; i < n; ++i) a[i][i] = 1; }
Matrix operator*(const Matrix &o) const {
Matrix r;
for (int i = 0; i < n; ++i)
for (int k = 0; k < n; ++k) if (a[i][k])
for (int j = 0; j < n; ++j)
r.a[i][j] = (r.a[i][j] + a[i][k] * o.a[k][j]) % MOD;
return r;
}
};
int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d%lld", &n, &m);
Matrix A;
for (int i = 0; i < n; ++i) {
int k, x; scanf("%d", &k);
while (k--) { scanf("%d", &x); A.a[i][x - 1] = 1; }
}
// 增廣矩陣 B: size n+1
Matrix B; int sz = n + 1;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j) B.a[i][j] = A.a[i][j];
for (int j = 0; j < n; ++j) B.a[n][j] = 1; // 最後一行全 1
B.a[n][n] = 1; // 保持累加
Matrix ans = qpow(B, m);
int sum = 0;
for (int j = 0; j <= n; ++j) sum = (sum + ans.a[n][j]) % MOD;
printf("%d\n", sum);
}
return 0;
}
矩陣乘法 O(N3),冪指數 M ≤ 105,log M ≈ 17,N ≤ 50,總運算量約 503 × 17 ≈ 2×106,極快。