CodeForces - 1117D 矩阵快速幂

该博客介绍了如何利用矩阵快速幂的方法解决CodeForces上的1117D问题。问题内容是求解一个全由1组成的字符串中,有多少种替换1为0的方式,使得0的子串长度恰好为m。博主解析了思路,提出状态转移方程,并阐述了通过矩阵优化将时间复杂度降低到O(m^3 log n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CodeForces - 1117D 矩阵快速幂


题目链接

题意

一个长度为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[i1]+f[im]。由于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} fnfn1fn2fn3fnm+1=11000001000001010000fn1fn2fn3fn4fnm
时间复杂度为 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值