CF1117D Magic Gems

原题传送门

题目大意:xht37有很多魔法宝石。每颗魔法宝石可以分解成 m m m颗普通宝石,魔法宝石和普通宝石都占据 1 1 1体积的空间,但普通宝石不能再被分解。

xht37想要使一些魔法宝石分解,使得所有宝石占据的空间恰好为 n n n单位体积。显然,一个魔法宝石分解后会占据 m m m体积空间,不分解的魔法宝石仍占据 1 1 1体积空间。

现在xht37想要求出有多少种分解方案,可以让最后得到的宝石恰好占据 n n n单位体积。两种分解方案不同当且仅当分解的魔法宝石数量不同,或者是所用的宝石的编号不同。

数据范围: 1 ≤ n ≤ 1 0 18 , 2 ≤ m ≤ 100 1 \le n\le 10^{18},2\le m\le 100 1n1018,2m100

思路:显然易见的 d p dp dp做法。用 d p [ i ] dp[i] dp[i]表示占据 i i i个体积空间时的分解方案,可以写出转移方程为:
{ d p [ i ] = d p [ i − 1 ] i < m d p [ i ] = d p [ i − 1 ] + d p [ i − m ] i ≥ m \begin{cases} dp[i]=dp[i-1]\qquad i\lt m \\ dp[i]=dp[i-1]+dp[i-m]\qquad i\ge m \end{cases} {dp[i]=dp[i1]i<mdp[i]=dp[i1]+dp[im]im
考虑到本题的 n ≤ 1 0 18 n\le 10^{18} n1018,线性递推必然超时,因此我们考虑用矩阵快速幂优化,把复杂度降低到 l o g log log级别。

我们考虑这样两个向量:
F ( n ) = [ d p n d p n − 1 ⋅ ⋅ ⋅ d p n − m + 1 ] F(n)=\begin{bmatrix} dp_n& dp_{n-1}& ···& dp_{n-m+1}\end{bmatrix} F(n)=[dpndpn1dpnm+1]
F ( n − 1 ) = [ d p n − 1 d p n − 2 ⋅ ⋅ ⋅ d p n − m ] F(n-1)=\begin{bmatrix} dp_{n-1}& dp_{n-2}& ···& dp_{n-m}\end{bmatrix} F(n1)=[dpn1dpn2dpnm]
如何找到一个矩阵 A A A,使得 F ( n ) = F ( n − 1 ) ∗ A F(n)=F(n-1)*A F(n)=F(n1)A

得到 A A A之后,我们把这个递推柿子一直写下去就可以得到: F ( n ) = F ( m − 1 ) ∗ A n − m + 1 F(n)=F(m-1)*A^{n-m+1} F(n)=F(m1)Anm+1

由于 F ( m − 1 ) = [ d p m − 1 d p m − 2 ⋅ ⋅ ⋅ d p 0 ] F(m-1)=\begin{bmatrix} dp_{m-1}& dp_{m-2}& ···& dp_{0}\end{bmatrix} F(m1)=[dpm1dpm2dp0] 由于 d p 0 = 1 dp_0=1 dp0=1,通过转移方程可得: F ( m − 1 ) = [ 1 1 ⋅ ⋅ ⋅ 1 1 ] F(m-1)=\begin{bmatrix} 1& 1& ···& 1& 1\end{bmatrix} F(m1)=[1111]为一个已知的全 1 1 1向量

那我们通过矩阵快速幂求出 A n − m + 1 A^{n-m+1} Anm+1之后就可以得到 F ( n ) F(n) F(n),而它的第一项就是所要求的答案 d p n dp_n dpn

下面我们讲一下如何求出矩阵 A A A,我们由转移方程可以得到:
{ d p [ n ] = 1 ∗ d p [ n − 1 ] + 1 ∗ d p [ n − m ] d p [ n − 1 ] = 1 ∗ d p [ n − 1 ] ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ d p [ n − m ] = 1 ∗ d [ n − m ] \begin{cases} dp[n]=1*dp[n-1]+1*dp[n-m]\\ dp[n-1]=1*dp[n-1]\\ \qquad\qquad······\\ dp[n-m]=1*d[n-m] \end{cases} dp[n]=1dp[n1]+1dp[nm]dp[n1]=1dp[n1]dp[nm]=1d[nm]
显然这个 m × m m\times m m×m的矩阵 A A A的各行各列值即为上面 m m m个柿子的系数,得:
A = [ 1 0 0 ⋅ ⋅ ⋅ 0 0 1 1 0 0 ⋅ ⋅ ⋅ 0 0 0 0 1 0 ⋅ ⋅ ⋅ 0 0 0 0 0 1 ⋅ ⋅ ⋅ 0 0 0 ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0 0 0 ⋅ ⋅ ⋅ 0 0 1 ] A=\begin{bmatrix} 1& 0& 0& ··· &0 &0 &1 \\ 1& 0& 0& ··· &0 &0 &0 \\ 0& 1& 0& ··· &0 &0 &0 \\ 0& 0& 1& ··· &0 &0 &0 \\ ···&···&···&···&···&···&···\\ 0& 0& 0& ··· &0 &0 &1 \\ \end{bmatrix} A=110000010000010000000000010001
至此,本题就可以在 O ( m 3 l o g n ) O(m^3logn) O(m3logn)的复杂度过了

PS:注意 n < m n<m n<m时,直接输出 1 1 1即可,不单独讨论的话,会超时…

Code:

#include <bits/stdc++.h>
using namespace std;
const int N=1e2+10,mod=1e9+7;
typedef long long ll;
ll n,m;
struct Mat{
    ll mat[N][N];
    Mat() {memset(mat,0,sizeof(mat));}
    Mat operator*(const Mat &b)const {
        Mat res;
        for(int i=0;i<100;i++){
            for(int j=0;j<100;j++){
                for(int k=0;k<100;k++){
                    res.mat[i][j]+=mat[i][k]*b.mat[k][j]%mod;
                    res.mat[i][j]%=mod;
                }
            }
        }
        return res;
    }
}base,ans;
void mat_power(ll k){
    for(;k;k>>=1){
        if(k&1) ans=ans*base;
        base=base*base;
    }
    return ;
}
int main(){
    scanf("%lld%lld",&n,&m);
    if(n<m) cout<<"1"<<endl;
    else{
        for(int i=0;i<m;i++) ans.mat[0][i]=1;
        base.mat[0][0]=base.mat[0][m-1]=1; 
        for(int i=1;i<m;i++) base.mat[i][i-1]=1;
        mat_power(n-m+1);
        printf("%lld",ans.mat[0][0]);
    }
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不会数据库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值