整數拆分


### 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();
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM