[学习笔记] 常系数线性递推

结论是,若 ∑ i = 0 k a i h n − i = 0 \sum_{i=0}^{k}a_ih_{n-i}=0 i=0kaihni=0,那么 h n = ∑ i = 0 k − 1 b i h i h_n=\sum_{i=0}^{k-1} b_i h_i hn=i=0k1bihi,其中 b b b x n x^n xn A T ( x ) A^T(x) AT(x)取模后的结果。
实在不想写多项式取模了……
所以写了个k<=2000的版本。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define mod 1000000007
#define lint long long
#define K 4010
#define LOG 40
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int b[LOG][K],a[K],h[K];
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
inline int mol(lint x) { return x%=mod,(x<0?x+mod:x); }
inline int fast_pow(int x,int k,int ans=1)
{   for(;k;k>>=1,x=(lint)x*x%mod) (k&1)?ans=(lint)ans*x%mod:0;return ans; }
inline int show(int *a,int n)
{
    for(int i=0;i<=n;i++) cerr<<a[i]<<" ";cerr<<endl;
    return 0;
}
inline int tms(int *a,int *b,int *c,int n)
{
    memset(c,0,sizeof(int)*(n*2+1));
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            (c[i+j]+=(lint)a[i]*b[j]%mod)%=mod;
    return 0;
}
inline int mol(int *a,int n,int *b,int m)
{
    for(int i=n;i>=m;i--)
    {
        int v=(lint)a[i]*fast_pow(b[m],mod-2)%mod;
        for(int j=m;j>=0;j--) a[i+j-m]=mol(a[i+j-m]-(lint)v*b[j]);
    }
    return 0;
}
inline int solve(int n,int *a,int m,int d)
{
    if(n<m) return memset(b[d],0,sizeof(int)*m),b[d][n]=1;
    solve(n/2,a,m,d+1),tms(b[d+1],b[d+1],b[d],m-1);
    if(n&1) for(int i=m*2-1;i>=1;i--) b[d][i]=b[d][i-1];
    if(n&1) b[d][0]=0;return mol(b[d],m*2-2+(n&1),a,m);
}
int main()
{
    int n=inn(),k=inn();a[0]=1;
    for(int i=1;i<=k;i++) scanf("%d",&a[i]),a[i]=mol(-a[i]);
    for(int i=0;i<k;i++) scanf("%d",&h[i]),h[i]=mol(h[i]);
    if(n<k) return !printf("%d\n",h[n]);
    for(int i=0;i<=k/2;i++) swap(a[i],a[k-i]);
    solve(n,a,k,0);int ans=0;
    for(int i=0;i<k;i++) (ans+=(lint)h[i]*b[0][i]%mod)%=mod;
    return !printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值