整数拆分

### Description

  现在定义函数\(F_m(n)\)表示将\(n\)表示为若干\(m\)的非负整数次幂的和的方案数

  定义\(G_m^k(n)\)\(k\)\(F_m(n)\)卷积起来的结果,现给定\(n,m,k\),求\(\sum\limits_{i=0}^n G_m^k(i)\),答案对\(10^9+7\)取模

  数据范围:\(0<=n<=10^{18},2<=m<=10^{18},1<=k<=20\)

  

Solution

  神仙题qwq

  考虑将这个问题形象化一点,说白了就是用\(1,m,m^2,m^3...m^w\)去凑\(n\),并且要求一个方案中前面的数不能比后面的数大,求方案数

  然后卷积起来的话就是。。每个数都有\(K\)个(为了与下面的变量区分开来,题目中的\(k\)统一写成大写),但是每个数中的\(k\)个是不一样的,可以算作不同的方案

  

  我们先考虑比较简单的\(k=1\)的情况:

  总共有\(log\)个可以用的数字\(1,m,m^2,...,m^w\),我们将这些数按照从小到大的顺序存到一个数组\(a\)里面去,我们令\(f[i][j]\)表示用了\(a\)中的前\(i\)个数字,当前的和为\(j\)的方案数,这个状态显然不够优秀因为第二维太大了

  考虑这样一个事情:假设我们现在只要求一个位置的值,比如说只用求\(F(n)\)\(m\)那个下标就不写了,默认就是\(m\),后面关于\(g\)函数的表示同),那么很明显并不是所有的\(F(x)\)都要用到的,我们可以只计算我们需要用到的位置的值

​  注意到在转移的时候,我们从小到大枚举\(i\)\(a_i\)一定是\(a_{i-1}\)的倍数,我们将第二维\(j\)写成\(k*a_{i-1}+r\)的形式(其中\(r<a_i\),其实也就是商和余数这样的形式),我们将\(j\)放在模\(a_{i-1}\)意义下看,会发现在转移的时候,因为加进来的\(a_i\)一定是\(a_{i-1}\)的倍数,所以模\(a_{i-1}\)的余数\(r\)是不会变的,也就是说如果说我们最后要求\(F(n)\),那么中间会用到的\(j\)一定是形如\(k*a^{i-1}+(n\%a_{i-1})\)

  所以我们可以将dp数组的第二维设成\(a_{i-1}\)前面的那个系数,记这个新的dp数组为\(h[i][j]\),表示可用前\(i\)\(a\)数组中的值,当前的和为\(j*a_{i-1}+(n\%a_{i-1})\)的方案数,那么有转移:

\[\begin{aligned} h[i][j]&=f[i][j*a_{i-1}+(n\% a_{i-1})]\\ &=\sum\limits_{k=0}^{j}f[i-1][(j-k)a_i+n\%a_i]\\ &=\sum\limits_{k=0}^{j}f[i-1][k*a_i+n\%a_i]\\ &=\sum\limits_{k=0}^{j}h[i-1][k\frac{a_i}{a_{i-1}}+\lfloor\frac{n\%a_i}{a_{i-1}}\rfloor]\\ \end{aligned} \]

​  然而现在的问题是。。这个第二维还是太大了

​  然后这个时候我们有一个结论(我不会证),当\(i\)固定的时候这个\(h[i]\)是一个关于第二维的\(i\)次多项式

​  那就。。暴力维护前\(i+1\)个值,第二维比较大的情况就直接插值就好了

​   

  但是现在的问题是,这样我们求出来的是单点的值,答案要我们求前缀和

​  这里又有一个很神的操作,我们给\(a\)数组里面多加一个\(1\),然后就相当于给这个多项式乘上了一个\((1+x+x^2+x^3+...)\),然后这个时候得到的就是前缀和了(具体实现上只要将\(h[0]\)初始化为每一位都是\(1\)就行)

  

​  复杂度是\(O(log_mn(nlog_mn+(log_mn)^2)\)的,其中\((log_mn)^2\)那项是拉格朗日插值的复杂度,还是有点爆炸,这个时候我们可以选择用线性插值但是我不会qwq

​  注意到这里插值的点\((x,y)\)中的\(x\)是连续的(从\(0\)\(i\)),所以我们预处理一下就可以做到\(O(log_mn)\)插值查询了(其实也不能说是把多项式的系数插了出来。。只是能算值而已不过那样在这题里面就够了)

  

​  代码大概长这样

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define ll long long
using namespace std;
const int N=2010,MOD=1e9+7;
ll a[N],pw[N];
ll fac[N],invfac[N];
int K,ans,lg,cnt;
ll m,n;
int plu(int x,int y){return (1LL*x+y)%MOD;}
int mul(int x,int y){return 1LL*x*y%MOD;}
int ksm(int x,int y){
    int ret=1,base=x;
    for (;y;y>>=1,base=mul(base,base))
        if (y&1) ret=mul(ret,base);
    return ret;
}
struct T1{/*{{{*/
    ll X[N],Y[N];
    ll pre[N],suf[N],fm[N];
    int n;
    void calc(){
        int tmp;
        for (int i=1;i<=n;++i){
            tmp=mul(invfac[i-1],invfac[n-i]);
            tmp=mul(tmp,Y[i]);
            if ((n-i)%2==1)
                fm[i]=(MOD-tmp)%MOD;
            else
                fm[i]=tmp;
        }
    }
    int get_val(ll x){
        if (x+1<=n) return Y[x+1];
        x%=MOD;
        int ret=0,tmp=1;
        pre[0]=1; suf[n+1]=1;
        for (int i=1;i<=n;++i) pre[i]=mul(pre[i-1],plu(x,MOD-X[i]));
        for (int i=n;i>=1;--i) suf[i]=mul(suf[i+1],plu(x,MOD-X[i]));
        for (int i=1;i<=n;++i)
            ret=plu(ret,mul(fm[i],mul(pre[i-1],suf[i+1])));
        return (ret+MOD)%MOD;
    }
}h[N];/*}}}*/
void prework(){
    pw[0]=1; lg=0;
    while (pw[lg]<=n/m) pw[lg+1]=pw[lg]*m,++lg;
    cnt=0;
    for (int i=0;i<=lg;++i)
        for (int j=1;j<=K;++j)
            a[++cnt]=pw[i];
    a[0]=1;
    fac[0]=1;
    for (int i=1;i<=cnt;++i) fac[i]=mul(fac[i-1],i);
    invfac[cnt]=ksm(fac[cnt],MOD-2);
    for (int i=cnt-1;i>=0;--i) invfac[i]=mul(invfac[i+1],i+1);
}
void dp(){
    h[0].X[1]=0; h[0].Y[1]=1; h[0].n=1; h[0].calc();
    ll r;
    for (int i=1;i<=cnt;++i){
        h[i].n=i+1;
        r=n%a[i]/a[i-1];
        for (int j=0;j<=i;++j){
            h[i].X[j+1]=j;
            h[i].Y[j+1]=plu(h[i].Y[j],h[i-1].get_val(a[i]/a[i-1]*j+r));
        }
        h[i].calc();
    }
    printf("%d\n",h[cnt].get_val(n/a[cnt]));
}
 
int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    scanf("%lld%lld%d",&n,&m,&K);
    ans=0;
    prework();
    dp();
}
posted @ 2018-11-29 20:36  yoyoball  阅读(695)  评论(6编辑  收藏  举报