Magic Gems魔法石

Magic Gems(递推+矩阵快速幂优化)

题面

Reziba has many magic gems. Each magic gem can be split into M normal gems. The amount of space each magic (and normal) gem takes is 1 unit. A normal gem cannot be split.

Reziba wants to choose a set of magic gems and split some of them, so the total space occupied by the resulting set of gems is N units. If a magic gem is chosen and split, it takes MM units of space (since it is split into M gems); if a magic gem is not split, it takes 1 unit.

How many different configurations of the resulting set of gems can Reziba have, such that the total amount of space taken is N units? Print the answer modulo 1000000007 ( 1 0 9 10^9 109+7). Two configurations are considered different if the number of magic gems Reziba takes to form them differs, or the indices of gems Reziba has to split differ.

Input

The input contains a single line consisting of 2 integers N and M ( 1 ≤ N ≤ 1 0 18 , 2 ≤ M ≤ 100 ) . (1 \le N \le 10^{18} , 2 \le M \le 100). (1N1018,2M100).

Output

Print one integer, the total number of configurations of the resulting set of gems, given that the total amount of space taken is N units. Print the answer modulo 1000000007 ( 1 0 9 10^9 109+7).

样例输入输出

Input

4 2

Output

5

Input

3 2

Output

3

Note

In the first example each magic gem can split into 2 normal gems, and we know that the total amount of gems are 4.

Let 1 denote a magic gem, and 0 denote a normal gem.

The total configurations you can have is:

  • 1111 (None of the gems split);
  • 0011 (First magic gem splits into 2 normal gems);
  • 1001 (Second magic gem splits into 2 normal gems);
  • 1100 (Third magic gem splits into 2 normal gems);
  • 0000 (First and second magic gems split into total 4 normal gems).

Hence, answer is 5.

题意:

(不得不说这题的题意真的很难理解)

Two configurations are considered different if the number of magic gems Reziba takes to form them differs, or the indices of gems Reziba has to split differ.

对于以上这句话我也是反复翻译却依旧不能理解

后来突然发现,这其实就是挨个取魔法石并判断是否分裂的过程

就拿note中的例子说(n=2,m=4):

1、1111 是指,每次取一个石头,每次都不分裂

2、0011 是指,取第一个石头,使其分裂,再取两个石头补满4个

3、1001 是指,取第一个石头,不分裂,取第二个石头,分裂,再取一个石头并不分裂,补满4个

4、1100 是指,取两个石头不分裂,再取一个石头分裂

5、0000 是指,先后取两个石头都分裂

这么理解就能完美解释note,并且也能解释两个样例输入输出

再一想,这题其实就有点像dp,取一个石头,判断是否分裂

如果我们设f[i] 为空间为i时的排列种类

那么其实f[i]=f[i-1]+f[i-m]就应该成立

因为我们取一个石头要判断是否分裂,如果分裂他的大小就为m,不分裂他的大小就为1

那我们只要知道空间为(i-m)时的排列种类f[i-m]和空间为(i-1)时的排列种类f[i-1]并将其累加就是取当前石头所有可能的情况

以此递推的话,答案很显然就是f[n]—— 即空间为n时所有的排列情况

矩阵推导

f(i)f(i-1)
f(i-1)f(i-2)
f(i-2)f(i-3)
f(i-m+2)f(i-m+1)
f(i-m+1)f(i-m)

左边就是需要递推出来的下一项

右边则是前一项

因为f(i)=f(i-1)+f(i-m) 所以转换矩阵的第一行的第一个数据与最后一个数据都是1,第一行其余都是0

之后可以发现从右边的第二行到倒数第二行和左边的第三行到最后一行,都存在一个倾斜的对于关系,只要在转换矩阵中适当赋1将其递推过去就行

于是我们得出
在这里插入图片描述

但是要注意,递推起点的初值,只有f(0)是有意义的,f(-1)f(-2)都是没有意义的

所以我们的递推起点的向量应该是{ f(m-1) , f(m-2) ,… f(0) }

所以递推次数应该是max(0, n - (m - 1))

再用矩阵快速幂就能解决

贴上AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod = 1e9 + 7;
template <typename T>
void read(T &x)
{
    x = 0;
    char ch = getchar();
    ll f = 1;
    while (!isdigit(ch))
    {
        if (ch == '-')
            f *= -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - 48;
        ch = getchar();
    }
    x *= f;
}
ll t, n, m;
const ll N = 109;
ll a[N][N], f[N];

void mul(ll a[109][109], ll f[109]) // m ^ 2
{
    ll c[109];
    memset(c, 0, sizeof(c));
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < m; j++)
        {
            c[i] = ((ll)c[i] + (ll)f[j] * (ll)a[j][i]) % mod;
        }
    }
    memcpy(f, c, sizeof(c));
}
void mulself(ll a[109][109]) // m^3
{
    ll c[109][109];
    memset(c, 0, sizeof(c));
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < m; j++)
        {
            for (int k = 0; k < m; k++)
            {
                c[i][j] = ((ll)c[i][j] + (ll)a[i][k] * (ll)a[k][j]) % mod;
            }
        }
    }
    memcpy(a, c, sizeof(c));
}

int main()
{
    read(n);
    read(m);
    a[0][0] = a[0][m - 1] = 1;
    for (int j = 1; j < m; j++)
    {
        a[j][j - 1] = 1;
    }
    for (int i = 0; i <= m - 1; i++)
    {
        f[i] = 1;
    }
    n -= (m - 1);
    n = max(0ll, n);
    for (; n; n >>= 1)
    {
        if (n & 1)
            mul(a, f);
        mulself(a);
    }
    printf("%lld\n", f[0]);
    return 0;
}
/*
+───────────+─────────────+
| f[0]      | f[n]        |
+───────────+─────────────+
| f[1]      | f[n-1]      |
| ...       | ...         |
| ...       | ...         |
| ...       | ...         |
| f[m - 1]  | f[n-m + 1]  |
|           |             |
|           |             |
|           |             |
|           |             |
+───────────+─────────────+
*/

如果有问题欢迎指出

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值