题目链接
题意
一个长度为n的串全是1,问有多少种方式用0替换1满足0的长度正好为m(可以没有)
思路
如果当前位置放1的话可以直接从前一位转移过来,如果放0的话则从i-m+1到i应该都是1,即从i-m转移过来。所以f[i]表示到第i位的方案数,则转移方程为
f
[
i
]
=
f
[
i
−
1
]
+
f
[
i
−
m
]
f[i] = f[i-1] + f[i-m]
f[i]=f[i−1]+f[i−m]。由于n很大所以做矩阵优化。
[
f
n
f
n
−
1
f
n
−
2
f
n
−
3
⋮
f
n
−
m
+
1
]
=
[
1
0
0
⋯
1
1
0
0
⋯
0
0
1
0
⋯
0
0
0
1
⋯
0
⋮
⋮
⋮
⋱
⋮
0
0
0
⋯
0
]
[
f
n
−
1
f
n
−
2
f
n
−
3
f
n
−
4
⋮
f
n
−
m
]
\begin{bmatrix}{f_{n}}\\{f_{n-1}}\\{f_{n-2}}\\{f_{n-3}}\\{\vdots}\\{f_{n-m+1}}\\\end{bmatrix} = \begin{bmatrix}{1}&{0}&{0}&{\cdots}&{1}\\{1}&{0}&{0}&{\cdots}&{0}\\{0}&{1}&{0}&{\cdots}&{0}\\{0}&{0}&{1}&{\cdots}&{0}\\{\vdots}&{\vdots}&{\vdots}&{\ddots}&{\vdots}\\{0}&{0}&{0}&{\cdots}&{0}\end{bmatrix} \begin{bmatrix}{f_{n-1}}\\{f_{n-2}}\\{f_{n-3}}\\{f_{n-4}}\\{\vdots}\\{f_{n-m}}\\\end{bmatrix}
⎣⎢⎢⎢⎢⎢⎢⎢⎡fnfn−1fn−2fn−3⋮fn−m+1⎦⎥⎥⎥⎥⎥⎥⎥⎤=⎣⎢⎢⎢⎢⎢⎢⎢⎡1100⋮00010⋮00001⋮0⋯⋯⋯⋯⋱⋯1000⋮0⎦⎥⎥⎥⎥⎥⎥⎥⎤⎣⎢⎢⎢⎢⎢⎢⎢⎡fn−1fn−2fn−3fn−4⋮fn−m⎦⎥⎥⎥⎥⎥⎥⎥⎤
时间复杂度为
O
(
m
3
log
n
)
O({m^3 \log n})
O(m3logn)
代码
#include <cstdio>
#include <vector>
const long long mod = 1000000007;
int m;
long long n, ans;
struct matrix {
int n, m;
std::vector<std::vector<long long> > mat;
matrix(int _n, int _m) : n(_n), m(_m) {
std::vector<long long> tmp(m);
for (int i = 0; i < n; ++i) mat.push_back(tmp);
set(0);
}
matrix& operator*=(const matrix& t) {
matrix res(n, t.m);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < t.m; ++j) {
for (int k = 0; k < m; ++k) {
res.mat[i][j] += (mat[i][k] * t.mat[k][j]);
res.mat[i][j] %= mod;
}
}
}
*this = res;
return *this;
}
void set(int k = 0) {
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
if (i == j)
mat[i][j] = k;
else
mat[i][j] = 0;
}
};
void init(matrix& a) {
a.set(0);
a.mat[0][0] = a.mat[0][a.m - 1] = 1;
for (int i = 1; i < a.n; ++i) a.mat[i][i - 1] = 1;
}
matrix ksm(matrix& a, long long k) {
matrix res(m, m);
res.set(1);
while (k) {
if (k & 1) res *= a;
a *= a;
k >>= 1;
}
return res;
}
int main() {
scanf("%lld%d", &n, &m);
if (n < m) return 0 * puts("1");
matrix a(m, m);
init(a);
matrix res = ksm(a, n - m + 1);
ans = 0;
for (int i = 0; i < m; ++i) ans = (ans + res.mat[0][i]) % mod;
printf("%lld\n", ans);
return 0;
}