常係數齊次線性遞推初探

添雅發表於2019-02-25

常係數齊次線性遞推式第n項的快速計算初探 XJB學後的XBJ胡扯

要做啥?

(f[n]=sum_{i=1}^{k} a[i]f[n-i] ;(nge k))(a[1,k],f[0,k-1])已經給出。

我會矩陣快速冪!

時間複雜度(O(k^3log n)​),其中(nle 10^9,kle32000​),emmm。

我會魔法!

設初始狀態矩陣為(S​),轉移矩陣為(A​)。轉移長得像這個樣子(四階情形)
[
egin{bmatrix}
f[4]\
f[3]\
f[2]\
f[1]
end{bmatrix}
=egin{bmatrix}
a[1]&a[2]&a[3]&a[4]\
1&0&0&0\
0&1&0&0\
0&0&1&0\
end{bmatrix}
egin{bmatrix}
f[3]\
f[2]\
f[1]\
f[0]
end{bmatrix}
]

構造序列(c​)滿足(A^n=sum_{i=0}^{k-1}c[i]A^i​),兩邊同時右乘(S​)
[
A^nS=(sum_{i=0}^{k-1}c[i]A^i)S=sum_{i=0}^{k-1}c[i]A^iS
]

我們的答案為((A^nS)[k-1,0]​),注意(A​)的特徵,可以發現,
[
(A^nS)[k-1,0]=sum_{i=0}^{k-1} c[i](A^iS)[k-1,0]=sum_{i=0}^{k-1}c[i]f[i]
]

所以只要構造出(c),我們就能(O(k))地完成答案的計算。

(k​)次多項式(G(x)​)滿足(G(A)=O​),則有
[
R(x)=x^nmod G(x)\
R(A)=A^n
]

顯然(R(x)​)是個(k-1​)次多項式,(c​)即為(R(x)​)的係數。

即需要構造一個滿足要求的(G(x)​)

我會更強力的魔法!

(k)階方陣(A)的特徵多項式 ​(p(lambda)=det(lambda I-A)),顯然(p(lambda))是一個(k)次多項式。

哈密爾頓–凱萊定理 (p(A)=O​)。(可以舉例子簡單驗證一下)

於是(G(x)​)就這樣出來了,推式子可得
[
p(lambda)=lambda^k-sum_{i=1}^ka[i]lambda^{k-i}
]

然後回代就做完了。取模的那部分是快速冪套多項式取模,所以總的時間複雜度為(O(klog klog n)​)

會魔法的實現

暫時留坑 題目傳送門

已填坑 on 26 Feb。參考程式碼如下

int n,k,p;
int a[N],f[N],c[N];
int g[N],invg[N]; 

void pomo(int*F,int*R) {
    static int Q[N],T[N];
    reverse(F,F+k+k-1);
    copy(F,F+k,T);
    nuthtr(T,p,1);
    for(int i=0; i<p; ++i) Q[i]=1LL*T[i]*invg[i]%P;
    nuthtr(Q,p,-1);
    fill(Q+k-1,Q+p,0);
    reverse(F,F+k+k-1);
    reverse(Q,Q+k-1); 
    nuthtr(Q,p,1);
    for(int i=0; i<p; ++i) Q[i]=1LL*g[i]*Q[i]%P;
    nuthtr(Q,p,-1);
    for(int i=0; i<k; ++i) R[i]=(F[i]-Q[i]+P)%P;
    fill(R+k,R+p,0);
    fill(Q,Q+p,0);
    fill(T,T+p,0);
}

int main() {
    n=read(),k=read();
    for(int i=1; i<=k; ++i) a[i]=(read()%P+P)%P;
    for(int i=0; i<k; ++i) f[i]=(read()%P+P)%P;
    for(int i=1; i<=k; ++i) g[k-i]=(P-a[i]); g[k]=1;
    
    for(p=1; p<=k; p<<=1); p<<=1;
    reverse(g,g+k+1);
    poin(g,invg,p);
    fill(invg+k+1,invg+p,0);
    reverse(g,g+k+1);
    nuthtr(g,p,1);
    nuthtr(invg,p,1);
    
    static int s[N];
    s[1]=1;
    c[0]=1;
    for(; n; n>>=1) {
        if(n&1) {
            nuthtr(c,p,1);
            nuthtr(s,p,1);
            for(int i=0; i<p; ++i) c[i]=1LL*c[i]*s[i]%P;
            nuthtr(c,p,-1);
            nuthtr(s,p,-1);
            pomo(c,c);
        }
        nuthtr(s,p,1);
        for(int i=0; i<p; ++i) s[i]=1LL*s[i]*s[i]%P;
        nuthtr(s,p,-1);
        pomo(s,s);
    }
    int ans=0;
    for(int i=0; i<k; ++i) ans=(ans+1LL*c[i]*f[i])%P;
    printf("%d
",ans);
    return 0;
}

相關文章