D - K-based Numbers. Version 3 (矩阵快速幂+dp)

Let’s consider K-based numbers, containing exactly N digits. We define a number to be valid if its K-based notation doesn’t contain two successive zeros. For example:

  • 1010230 is a valid 7-digit number;
  • 1000198 is not a valid number;
  • 0001235 is not a 7-digit number, it is a 4-digit number.

Given three numbers NK and M, you are to calculate an amount of valid K based numbers, containing N digits modulo M.

You may assume that 2 ≤ NKM ≤ 10 18.

Input

The numbers NK and M in decimal notation separated by the line break.

Output

The result in decimal notation.

Example

input output
2
10
100
90

题解:状态转移方程:dp[1]=k-1,dp[2]=(k-1)*k,dp[i]=(k-1)*(dp[i-1]+dp[i-2])

我们设矩阵A:

k-1    k-1 

1       0         为规律矩阵,即恒定矩阵。

初始矩阵B:

k-1    0

1       0

那么B[1][1]=dp[1],这里我们为了方便假设dp[0]=B[2][1]=0.

那么我们往B的左边乘上1个矩阵A得矩阵C[1][1]=dp[2],C[2][1]=dp[1].

如此迭代下去,即可得到最终结果,由于每一次都是左乘规律矩阵A,故我们可以由快速幂来优化。

快速幂理解:1次幂进来:得A*B=C,则dp[2]=C[1][1],二次幂进来得A*C=D,则dp[3]=D[1][1]……那么m-1次幂进来得到dp[m]=X[1][1].

还有由于这个版本数据比较大,则用到了矩阵快速幂的知识。

#include<iostream>
#include<cmath>
#include<cstring>
#include<stdio.h>
using namespace std;
typedef long long ll;
ll n,k,mod;
const int MOD=1e9+7;
struct Mat
{
    ll mat[5][5];
    void init()
    {
        memset(mat,0,sizeof(mat));
    }
}a,b;
ll quickmul(ll a,ll b)
{
    ll ans=0;
    while(b)
    {
        if(b&1)
            ans=(ans+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return ans;
}
Mat operator *(Mat a,Mat b)
{
    Mat c;
    memset(c.mat,0,sizeof(c.mat));
    for(int i=1;i<=2;i++)
    {
        for(int j=1;j<=2;j++)
        {
            for(int k=1;k<=2;k++)
            {
                c.mat[i][j]=(c.mat[i][j]+quickmul(a.mat[i][k],b.mat[k][j]))%mod;
            }
        }
    }
    return c;
}
Mat quickpow(ll m)  //m为1进来得到dp[2]。
{
    Mat res,a;
    a.init();
    res.init();
    res.mat[1][1]=k-1;
    res.mat[2][1]=1;
    a.mat[1][1]=a.mat[1][2]=k-1;
    a.mat[2][1]=1;
    while(m)
    {
        if(m&1)
            res=a*res;
        a=a*a;
        m>>=1;
    }
    return res;
}
int main()
{

    scanf("%lld%lld%lld",&n,&k,&mod);
    Mat x=quickpow(n-1);
    cout<<x.mat[1][1]<<endl;
    return 0;
}

在这里也加上version1的代码吧:

#include<iostream>
using namespace std;
int main()
{
    int n,k;
    int dp[111];
    cin>>n>>k;
    dp[1]=k-1;  //除了0都可选
    dp[2]=(k-1)*k; //第一位除了0都可选,第二位都可选,乘法原理
    for(int i=3;i<=n;i++)
        dp[i]=(k-1)*(dp[i-1]+dp[i-2]);
    cout<<dp[n]<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43824158/article/details/87908052