UOJ Logo Ignatz的博客

博客

zoj 3216

2015-12-21 13:06:47 By Ignatz

$zoj\ 3216$

题目大意

给出$n$,$k$,$(1 \leq n \leq 10^9,1 \leq k \leq 30)$。$f_n$表示将$n$分为数个不小于$k$的正整数的和的方案数,数字相同顺序不同的方案不算同一个,求$f_n$。

讨论方案的第一个数$x$,有

$$ f_n= \left\{ \begin{array}{ll} f_{n-1} & \textrm{if $x>k$} \\ f_{n-k} & \textrm{if $x=k$} \end{array} \right. $$

$\therefore f_i=f_{i-1}+f_{i-k}$。

构造$\mathrm{F(i)}$,$\mathrm{R}$。

$$ \mathrm{F(i)}= \left( \begin{array}{cccc} f_i & f_{i-1} & \ldots & f_{i-k+1} \end{array} \right) $$

$$ \mathrm{R}= \left( \begin{array}{ccccc} 1 & 1 & 0 & \ldots & 0 \\ 0 & 0 & 1 & \ldots & 0\\ 0 & 0 & 0 & \ddots & 0\\ \vdots & \vdots & \vdots & \ldots & 1\\ 1 & 0 & 0 & 0 & 0 \end{array} \right) $$

$$ n_0=k, \mathrm{F(k)}= \left( \begin{array}{cccc} 1 & 0 & \ldots & 0 \end{array} \right) $$

Code

#include <cstdio>
#include <cstring>
#define rep(i,x,y) for (int i=x; i<=y; ++i)

typedef long long ll;
const ll mod=1000000007;
struct mat
{
    int n,m;
    ll v[35][35];
    mat(){}
    mat(int a,int b):n(a),m(b)
    {
        memset(v,0,sizeof(v));
    }
    ll *operator [] (int x)
    {
        return v[x];
    }
} r,s;
int n,m,tc;

inline void operator *= (mat &a,mat b)
{
    ll t[35];
    rep(i,0,a.n)
    {
        memset(t,0,sizeof(t));
        rep(j,0,a.m)
            rep(k,0,a.m)
                t[j]=(t[j]+a[i][k]*b[k][j]%mod)%mod;
        memcpy(a[i],t,sizeof(t));
    }
}

int main()
{
    for (scanf("%d",&tc); tc--;)
    {
        scanf("%d%d",&n,&m),--m;
        if (n<=m)
        {
            puts("0");
            continue;
        }
        r=mat(m,m),++r[0][0],++r[m][0];
        rep(i,1,m)
            r[i-1][i]=1;
        s=mat(0,m),s[0][0]=1;
        for (int i=n-m-1; i; i>>=1,r*=r)
            if (i&1)
                s*=r;
        printf("%lld\n",s[0][0]);
    }
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。