CF1117D - Magic Gems (矩阵快速幂)
Description
题目大意是,一个魔法宝石可以分裂成M个普通宝石。你有若干个魔法宝石,问你生成N个宝石(包括魔法和普通宝石)的有多少种情况。
思路
设f[N]为生成N个宝石的方案数,它可以来自N-1个宝石加一个魔法宝石,或N-M个宝石加M个普通宝石(由一个魔法宝石分裂而来)。
所以可以得到递推式:
f[N] = f[N - M] + f[N - 1]
f[0] = f[1] = 1
f[n] = 0 (n < 0)
然后就可以用矩阵快速幂求了。
如何列相应矩阵,可以见我之前的一篇文章。
const int N = 1e3 + 10;
const int M = 1000000007;
const double eps = 1e5;
ll f[N];
ll init(ll n, ll m) {
if(n == 1 || n == 0) {
return f[1] = 1;
}
if(n < 1) return 0;
if(f[n]) return f[n];
return f[n] = (init(n - 1, m) + init(n - m, m)) % M;
}
ll A[N][N];
ll B[N][N];
ll tmp[N][N];
void mul1(int n) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
tmp[i][j] = 0;
for(int k = 1; k <= n; k++) {
tmp[i][j] += A[i][k] * B[k][j];
tmp[i][j] %= M;
}
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
B[i][j] = tmp[i][j];
}
}
}
void mul2(int n) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
tmp[i][j] = 0;
for(int k = 1; k <= n; k++) {
tmp[i][j] += A[i][k] * A[k][j];
tmp[i][j] %= M;
}
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
A[i][j] = tmp[i][j];
}
}
}
void qpow(ll n, int m) {
while(n) {
if(n & 1) {
mul1(m); //B = B * A
}
mul2(m); //A = A * A
n = n >> 1;
}
}
int main() {
IOS;
ll n, m;
cin >> n >> m;
init(m, m);
for(int i = 1; i <= m; i++) {
B[i][i] = 1;
}
A[1][1] = 1;
A[1][m] = 1;
for(int i = 1; i < m; i++) A[i + 1][i] = 1;
if(n <= m) {
cout << f[n] << endl;
} else {
qpow(n - m, m);
ll ans = 0;
for(int i = 1; i <= m; i++) {
ans = (ans + B[1][i] * f[m - i + 1]) % M;
}
cout << ans << endl;
}
}