1. 程式人生 > >【[六省聯考2017]組合數問題】

【[六省聯考2017]組合數問題】

好水啊

但是我傻啊

我們設\(dp[i][j]=\sum_{t=0}^{∞}\binom{ik}{j+tk}\)

根據組合數萬年不變的遞推式\(\binom{n}{m}=\binom{n-1}{m-1}+\binom{n-1}{m}\)

我們有\(dp[i][j]=dp[i-1][j]+dp[i-1][(j-1+k)\%k]\)

顯然這個柿子可以用矩陣優化到\(log\)

於是就沒有了

有一個坑點就是當\(k=1\)的時候實際上是有\(dp[i][0]=2*dp[i-1][0]\),於是構造矩陣的時候小心一下就可以了

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
inline int read()
{
    re char c=getchar();
    re int x=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9')
        x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x;
}
LL n,k,P,r;
LL ans[51][51],a[51][51];
inline void did_a()
{
    LL mid[51][51];
    for(re int i=0;i<k;i++)
        for(re int j=0;j<k;j++)
            mid[i][j]=a[i][j],a[i][j]=0;
    for(re int i=0;i<k;i++)
        for(re int j=0;j<k;j++)
            for(re int p=0;p<k;p++)
                a[i][j]=(a[i][j]+(mid[i][p]*mid[p][j])%P)%P;
}
inline void did_ans()
{
    LL mid[51][51];
    for(re int i=0;i<k;i++)
        for(re int j=0;j<k;j++)
            mid[i][j]=ans[i][j],ans[i][j]=0;
    for(re int i=0;i<k;i++)
        for(re int j=0;j<k;j++)
            for(re int p=0;p<k;p++)
                ans[i][j]=(ans[i][j]+(mid[i][p]*a[p][j])%P)%P;
}
inline void out()
{
    for(re int i=0;i<k;i++)
    {
        for(re int j=0;j<k;j++)
            printf("%d ",a[i][j]);
        putchar(10);
    }
}
inline void quick(LL b)
{
    while(b)
    {
        if(b&1ll) did_ans();
        b>>=1ll;
        did_a();
    }
}
int main()
{
    n=read(),P=read(),k=read(),r=read();
    for(re int i=0;i<k;i++)
        ans[i][i]=1;
    for(re int i=1;i<k;i++)
        a[i][i]=1,a[i][i-1]=1;
    a[0][0]=1,a[0][k-1]+=1;
    quick(n*k);
    printf("%lld\n",ans[r][0]);
    return 0;
}