• 利用Cayley-Hamilton theorem 优化矩阵线性递推


    平时有关线性递推的题,很多都可以利用矩阵乘法来解决。 时间复杂度一般是O(K3logn)因此对矩阵的规模限制比较大。

    下面介绍一种利用利用Cayley-Hamilton theorem加速矩阵乘法的方法。

     

    Cayley-Hamilton theorem:

    记矩阵A的特征多项式为f(x)。 则有f(A)=0.

    证明可以看 维基百科 https://en.wikipedia.org/wiki/Cayley–Hamilton_theorem#A_direct_algebraic_proof

    另外我在高等代数的课本上找到了 证明(和维基百科里的第一种证明方法是一样的)

     

     

     

    下面介绍几个 利用可以这个定理解决的题目:

    1. project euler 258    

    显然可以用矩阵乘法来做。下面讲一下怎么利用Cayley-Hamilton theorem 来优化: 详细的论述可以参考这篇

    设M为K阶矩阵,主要思想就是将$M^n$表示为 $b_0 M^0 + b_1 M^1 + cdots b_{K-1}M^{K-1}$这样的形式.

    根据Cayley-Hamilton theorem $M^K = a_0 M^0 + a_1 M^1 + cdots a_{K-1}M^{K-1}$    

    由于转移矩阵的特殊性,不难证明$a_i$恰好是线性递推公式里的系数。

    假设我们已经将$M^n$表示为 $b_0 M^0 + b_1 M^1 + cdots b_{K-1}M^{K-1}$这样的形式,不难得到$M^{n+1}$的表示法。只要将$M^n$乘个M之后得到的项中$M^K$拆成小于K次的线性组合就好了。  这样我们可以预处理出$M^0 M^1 cdots M^{2K-2}$的表示法。

    对于次数更高的, $M^{i+j}=M^i*M^j$  可以看成是两个多项式的乘法。 利用快速幂 可以在O(K2logn)的时间求出$M^n$的表示法.

     

    另外有一个优化常数的trick, 可以预处理出$M^1$ $M^2$  $M^4$  $M^8$....  $M^{2^r}$这些项, 对于$M^n$只要根据二进制位相应的乘上这些项就好了。 这样做比直接做快速幂快一倍(少了一半的多项式乘法操作)。

     

    参考代码:

     1 //ans=12747994 
     2 #include <cstdio>
     3 #include <iostream>
     4 #include <queue>
     5 #include <algorithm>
     6 #include <cstring>
     7 #include <set>
     8 using namespace std;
     9 
    10 #define N 2000
    11 typedef long long ll;
    12 
    13 const int Mod=20092010;
    14 int a[N],f[N<<1];
    15 int k=2000;
    16 
    17 
    18 
    19 //基本思想是把A^n 表示成A^0  A^1 A^2 ... A^(k-1)的线性组合
    20 //A^(p+q)可看成两个多项式相乘,只要实现预处理出A^0  A^1 A^2 ... A^(2k-2)的多项式表示法 
    21 //A^k可以根据特征多项式的性质得到 ,A^(n+1)次可以从A^n次得到  根据这个来预处理 
    22 struct Poly
    23 {
    24     int b[N];
    25 }P[N<<1];
    26 
    27 
    28 Poly operator * (const Poly &A,const Poly &B)
    29 {
    30     Poly ans; memset(ans.b,0,sizeof(ans.b));
    31     for (int i=0;i<=2*k-2;i++)
    32     {
    33         int res=0;
    34         for (int j=max(0,i-k+1);j<k && j<=i;j++)
    35         {
    36             res+=1ll*A.b[j]*B.b[i-j]%Mod;
    37             if (res>=Mod) res-=Mod;
    38         }
    39         if (i<k) {ans.b[i]=res; continue;}
    40         
    41         //把次数大于等于k的搞成小于k 
    42         for (int j=0;j<k;j++)   
    43         {
    44             ans.b[j]+=1ll*res*P[i].b[j]%Mod;
    45             if (ans.b[j]>=Mod) ans.b[j]-=Mod;
    46         }
    47     }
    48     return ans;
    49 }
    50 
    51 Poly Power_Poly(ll p)
    52 {
    53     if (p<=2*k-2) return P[p];
    54     
    55     Poly ans=P[0],A=P[1];
    56     for (;p;p>>=1)
    57     {
    58         if (p&1) ans=ans*A;
    59         A=A*A;
    60     }
    61     return ans;
    62 }
    63 
    64 int main()
    65 {
    66     freopen("in.in","r",stdin);
    67     freopen("out.out","w",stdout);
    68     
    69     //f[n]=a[k-1]f[n-1]....a[0]f[n-k]
    70     a[0]=a[1]=1; ll n; n=1e18;
    71     for (int i=0;i<k;i++) P[i].b[i]=1;
    72     
    73     //P[k]=a[0]P[0]+a[1]P[1]+....a[k-1]P[k-1]
    74     for (int i=0;i<k;i++) P[k].b[i]=a[i]; 
    75     
    76     //Calculate P[k+1]...P[2k-2]
    77     //using P[n+1]=a[0]*b[k-1]+ (a[1]*b[k-1]+b[0]) + (a[2]*b[k-1]+b[1]) +...(a[k-1]*b[k-1]+b[k-2])
    78     for (int j=k+1;j<=2*k-2;j++)
    79     {
    80         P[j].b[0]=1ll*a[0]*P[j-1].b[k-1]%Mod;
    81         for (int i=1;i<k;i++)
    82             P[j].b[i]=(1ll*a[i]*P[j-1].b[k-1]%Mod+P[j-1].b[i-1])%Mod;
    83     }
    84     
    85     Poly tmp=Power_Poly(n-k+1); int ans=0;
    86     
    87     for (int i=0;i<k;i++) f[i]=1;
    88     for (int i=k;i<=2*k-2;i++) f[i]=(f[i-1999]+f[i-2000])%Mod;
    89     
    90     //A^n*X=b[0]*A^0*X+b[1]*A^1*X+...b[k-1]*A^(k-1)*X      A^i*X= {f[k-1+i]  f[k-2+i]...  f[0+i]}
    91     for (int i=0;i<k;i++)
    92     {
    93         ans+=1ll*tmp.b[i]*f[k-1+i]%Mod;
    94         if (ans>=Mod) ans-=Mod;
    95     }
    96     printf("%d
    ",ans);
    97     return 0;
    98 }
    View Code

     

     


     

    2.设,求的值。其中

         

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1229 

     

    首先这个题可以用扰动法 搞出一个关于k的递推公式,在O(k2)的时间解决。

    具体可以参考这篇。 虽然不是同一个式子,但是方法是一样的,扰动法在《具体数学》上也有介绍。因此本文不再赘述。

    给个AC代码供参考:

      1 #include <cstdio>
      2 #include <iostream>
      3 #include <queue>
      4 #include <algorithm>
      5 #include <cstring>
      6 #include <set>
      7 using namespace std;
      8 
      9 #define N 2010
     10 typedef long long ll;
     11 const int Mod=1000000007;
     12 
     13 int k;
     14 ll n,r;
     15 int f[N],inv[N];
     16 int fac[N],fac_inv[N];
     17 
     18 int C(int x,int y)
     19 {
     20     if (y==0) return 1;
     21     if (y>x) return 0;
     22     
     23     int res=1ll*fac[x]*fac_inv[y]%Mod;
     24     return 1ll*res*fac_inv[x-y]%Mod;
     25 }
     26 
     27 int Power(ll a,ll p)
     28 {
     29     int res=1; a%=Mod;
     30     for (;p;p>>=1)
     31     {
     32         if (p&1) res=1ll*res*a%Mod;
     33         a=a*a%Mod;
     34     }
     35     return res;
     36 }
     37 
     38 int Solve1()
     39 {
     40     f[0]=n;  int t=n+1;
     41     for (int i=1;i<=k;i++)
     42     {
     43         f[i]=t=1ll*t*(n+1)%Mod;
     44         for (int j=0;j<i;j++)
     45         {
     46             f[i]+=Mod-1ll*C(i+1,j)*f[j]%Mod;
     47             if (f[i]>=Mod) f[i]-=Mod;
     48         }
     49         f[i]--; if (f[i]<0) f[i]+=Mod;
     50         f[i]=1ll*f[i]*inv[i+1]%Mod;
     51     }
     52     return f[k];
     53 }
     54 
     55 
     56 int Solve2()
     57 {
     58     f[0]=Power(r,n+1)-r%Mod;
     59     if (f[0]<0) f[0]+=Mod;
     60     f[0]=1ll*f[0]*Power(r-1,Mod-2)%Mod;
     61     
     62     for (int i=1;i<=k;i++)
     63     {
     64         f[i]=1ll*Power(n+1,i)*Power(r,n+1)%Mod;
     65         f[i]-=r%Mod; if (f[i]<0) f[i]+=Mod;
     66         
     67         int tmp=0;
     68         for (int j=0;j<i;j++)
     69         {
     70             tmp+=1ll*C(i,j)*f[j]%Mod;
     71             if (tmp>=Mod) tmp-=Mod;
     72         }
     73         f[i]-=1ll*(r%Mod)*tmp%Mod;
     74         if (f[i]<0) f[i]+=Mod;
     75         f[i]=1ll*f[i]*Power(r-1,Mod-2)%Mod;
     76         //cout<<i<<" "<<f[i]<<endl;
     77     }
     78     return f[k];
     79 }
     80 
     81 
     82             
     83 int main()
     84 {
     85     //freopen("in.in","r",stdin);
     86     //freopen("out.out","w",stdout);
     87     
     88     inv[1]=1; for (int i=2;i<N;i++) inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
     89     fac[0]=1; for (int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%Mod;
     90     fac_inv[0]=1; for (int i=1;i<N;i++) fac_inv[i]=1ll*fac_inv[i-1]*inv[i]%Mod;
     91     
     92     int T; scanf("%d",&T);
     93     while (T--)
     94     {
     95         cin >> n >> k >> r;
     96         if (r==1) printf("%d
    ",Solve1());
     97         else printf("%d
    ",Solve2());
     98     }
     99     
    100     return 0;
    101 }
    View Code

    本文要介绍的是利用优化后的矩阵乘法来解决本题(也许是我写的太丑,常数巨大,极限数据要跑6s,不能AC本题,但是可以拿来作为练习)

    首先要知道怎么构造矩阵。

     

    设$F(n,j)=n^j r^n$ 列向量 $X=[S_{n-1} , F(n,k) , F(n,k-1), cdots , F(n,0)]^T$

     

     

    更加详细的题解可以参考这篇. 上面的推导过程的出处(矩阵实在不会用latex公式打,就只好copy了。)

    我的方法和他略有不同, 我的列向量第一个元素是$S_{n-1}$ ,因此我的转移矩阵的第一行是1,1,0,0...0  其他都是一样的。

     

    另外我猜测这题的这个式子和国王奇遇记那题一样,应该也是一个什么玩意乘上一个多项式,可以用多项式插值的办法来求(排行榜前面的代码跑的都很快,目测是O(K)的)。

    比较懒,懒得去想了。。有兴趣的朋友可以去搞一搞?

    我的代码(最后几个点TLE,用来练习 优化矩阵乘法):

      1 //http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1229
      2 #include <iostream>
      3 #include <cstdio>
      4 #include <cmath>
      5 #include <cstring>
      6 #include <algorithm>
      7 #include <vector>
      8 #include <map>
      9 #include <cstdlib>
     10 #include <set>
     11 using namespace std;
     12 
     13 #define X first
     14 #define Y second
     15 #define Mod 1000000007
     16 #define N 2010
     17 typedef long long ll;
     18 typedef pair<int,int> pii;
     19 
     20 int K;
     21 ll n,r;
     22 int fac[N],fac_inv[N],S[N];
     23 
     24 struct Poly
     25 {
     26     int cof[N];
     27     void print()
     28     {
     29         for (int i=0;i<K;i++) printf("%d ",cof[i]);
     30         printf("
    ");
     31     }
     32 }M[N<<1],R[62];
     33 
     34 int a[N],b[N];
     35 
     36 int C(int x,int y)
     37 {
     38     if (y==0) return 1;
     39     
     40     int res=1ll*fac[x]*fac_inv[y]%Mod;
     41     return 1ll*res*fac_inv[x-y]%Mod;
     42 }
     43 
     44 int Power(ll x,ll p)
     45 {
     46     int res=1; x%=Mod;
     47     for (;p;p>>=1)
     48     {
     49         if (p&1) res=1ll*res*x%Mod;
     50         x=x*x%Mod;
     51     }
     52     return res;
     53 }
     54 
     55 
     56 Poly operator * (const Poly &A,const Poly &B)
     57 {
     58     Poly ans; memset(ans.cof,0,sizeof(ans.cof));
     59     
     60     for (int i=0;i<2*K-1;i++)
     61     {
     62         int res=0;
     63         for (int j=max(0,i-K+1);j<=i && j<K;j++)
     64         {
     65             res+=1ll*A.cof[j]*B.cof[i-j]%Mod;
     66             if (res>=Mod) res-=Mod;
     67         }
     68         if (i<K) {ans.cof[i]=res; continue;}
     69         
     70         for (int j=0;j<K;j++)
     71         {
     72             ans.cof[j]+=1ll*res*M[i].cof[j]%Mod;
     73             if (ans.cof[j]>=Mod) ans.cof[j]-=Mod;
     74         }
     75     }
     76     return ans;
     77 }
     78 
     79 Poly Poly_Power(ll p)
     80 {
     81     if (p<=2*K-2) return M[p];
     82     
     83     Poly res=M[0];
     84     //p&(1ll<<j)  千万别忘了1后面的ll !!! 
     85     for (int j=0;j<=60;j++) if (p&(1ll<<j)) res=res*R[j];
     86     return res;
     87 }
     88 
     89 
     90 void Init()
     91 {
     92     memset(a,0,sizeof(a));
     93     memset(b,0,sizeof(b));
     94     
     95     
     96     //求出特征多项式 M^(K+2)=a[0]*M^0 +  a[1]*M^1 + ... a[K+1]*M^(K+1) 
     97     int op;
     98     if (r==1)   //特征多项式f(x)= (x-1)^(K+2)
     99     {
    100         for (int i=0;i<K+2;i++)
    101         {
    102              op=(K-i)&1? -1:1;
    103              a[i]=op*C(K+2,i);
    104              a[i]=-a[i];
    105              if (a[i]<0) a[i]+=Mod;
    106         }
    107     }
    108     else   //特征多项式f(x)= (x-1) * (x-r)^(K+1)
    109     {
    110         for (int i=0;i<=K+1;i++)
    111         {
    112              op=(K+1-i)&1? -1:1;
    113              b[i]=1ll*op*C(K+1,i)*Power(r,K+1-i)%Mod;
    114              if (b[i]<0) b[i]+=Mod;
    115         }
    116         for (int i=1;i<K+2;i++) a[i]=b[i-1];
    117         for (int i=0;i<=K+1;i++) 
    118         {
    119             a[i]-=b[i];
    120             a[i]=-a[i];
    121             if (a[i]<0) a[i]+=Mod;
    122         }
    123     }
    124     
    125     K+=2;   //矩阵的规格
    126     
    127     
    128     //预处理M^0...M^(2K-2) 
    129     
    130     memset(M,0,sizeof(M));
    131     
    132     for (int i=0;i<K;i++) M[i].cof[i]=1;
    133     for (int i=0;i<K;i++) M[K].cof[i]=a[i];
    134     
    135     for (int i=K+1;i<=2*K-2;i++)
    136     {
    137         M[i].cof[0]=1ll*a[0]*M[i-1].cof[K-1]%Mod;
    138         for (int j=1;j<K;j++)
    139         {
    140             M[i].cof[j]=1ll*a[j]*M[i-1].cof[K-1]%Mod+M[i-1].cof[j-1];
    141             if (M[i].cof[j]>=Mod) M[i].cof[j]-=Mod;
    142         }
    143     }
    144      
    145     //预处理M^1 M^2 M^4 M^8 M^16... 
    146     R[0]=M[1];
    147     for (int i=1;i<=60;i++) R[i]=R[i-1]*R[i-1];
    148     
    149     S[0]=0;
    150     for (int i=1;i<K;i++) 
    151     {
    152         S[i]=1ll*Power(i,K-2)*Power(r,i)%Mod;
    153         S[i]+=S[i-1];
    154         if (S[i]>=Mod) S[i]-=Mod;
    155     }
    156 }
    157 
    158 
    159 int Solve()
    160 {
    161     Poly tmp=Poly_Power(n);
    162     
    163     int ans=0,t;
    164     
    165     for (int i=0;i<K;i++)
    166     {
    167         ans+=1ll*tmp.cof[i]*S[i]%Mod;
    168         if (ans>=Mod) ans-=Mod;
    169     }
    170     return ans;
    171 }
    172 
    173 int main()
    174 {
    175     //freopen("in.in","r",stdin);
    176     //freopen("out.out","w",stdout);    
    177     
    178     fac[0]=1;
    179     for (int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%Mod;
    180     fac_inv[N-1]=Power(fac[N-1],Mod-2);
    181     for (int i=N-2;i>=0;i--) fac_inv[i]=1ll*fac_inv[i+1]*(i+1)%Mod;
    182 
    183     
    184     int T; scanf("%d",&T);
    185     while (T--)
    186     {
    187         cin >> n >> K >> r;
    188         Init();
    189         printf("%d
    ",Solve());
    190     }
    191 
    192     
    193     return 0;
    194 }
    View Code

     

    3. hdu 3483   

       福利,和上面那题几乎完全是一样的,就是 范围小了很多。可以用来测试上面未通过的代码。   坑点就是 模数 最大是2e9,  最好开个long long, 否则int范围做加法就会爆。

     参考代码:

     

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cmath>
      4 #include <cstring>
      5 #include <algorithm>
      6 #include <vector>
      7 #include <map>
      8 #include <cstdlib>
      9 #include <set>
     10 using namespace std;
     11 
     12 #define X first
     13 #define Y second
     14 #define N 70
     15 typedef long long ll;
     16 typedef pair<int,int> pii;
     17 
     18 int K,Mod;
     19 ll n,r;
     20 ll S[N];
     21 ll cob[N][N];
     22 
     23 struct Poly
     24 {
     25     ll cof[N];
     26     void print()
     27     {
     28         for (int i=0;i<K;i++) printf("%d ",cof[i]);
     29         printf("
    ");
     30     }
     31 }M[N<<1],R[62];
     32 
     33 ll a[N],b[N];
     34 
     35 ll C(int x,int y)
     36 {
     37     return cob[x][y];
     38 }
     39 
     40 ll Power(ll x,ll p)
     41 {
     42     ll res=1; x%=Mod;
     43     for (;p;p>>=1)
     44     {
     45         if (p&1) res=1ll*res*x%Mod;
     46         x=x*x%Mod;
     47     }
     48     return res;
     49 }
     50 
     51 
     52 Poly operator * (const Poly &A,const Poly &B)
     53 {
     54     Poly ans; memset(ans.cof,0,sizeof(ans.cof));
     55     
     56     for (int i=0;i<2*K-1;i++)
     57     {
     58         ll res=0;
     59         for (int j=max(0,i-K+1);j<=i && j<K;j++)
     60         {
     61             res+=1ll*A.cof[j]*B.cof[i-j]%Mod;
     62             if (res>=Mod) res-=Mod;
     63         }
     64         if (i<K) {ans.cof[i]=res; continue;}
     65         
     66         for (int j=0;j<K;j++)
     67         {
     68             ans.cof[j]+=1ll*res*M[i].cof[j]%Mod;
     69             if (ans.cof[j]>=Mod) ans.cof[j]-=Mod;
     70         }
     71     }
     72     return ans;
     73 }
     74 
     75 Poly Poly_Power(ll p)
     76 {
     77     if (p<=2*K-2) return M[p];
     78     
     79     Poly res=M[0];
     80     //p&(1ll<<j)  千万别忘了1后面的ll !!! 
     81     for (int j=0;j<=60;j++) if (p&(1ll<<j)) res=res*R[j];
     82     return res;
     83 }
     84 
     85 
     86 void Init()
     87 {
     88     memset(a,0,sizeof(a));
     89     memset(b,0,sizeof(b));
     90     
     91     cob[0][0]=1;
     92     for (int i=1;i<N;i++)
     93     {
     94         cob[i][0]=1;
     95         for (int j=1;j<N;j++)
     96             cob[i][j]=(cob[i-1][j-1]+cob[i-1][j])%Mod;
     97     }
     98     
     99     //求出特征多项式 M^(K+2)=a[0]*M^0 +  a[1]*M^1 + ... a[K+1]*M^(K+1) 
    100     int op;
    101     if (r==1)   //特征多项式f(x)= (x-1)^(K+2)
    102     {
    103         for (int i=0;i<K+2;i++)
    104         {
    105              op=(K-i)&1? -1:1;
    106              a[i]=op*C(K+2,i);
    107              a[i]=-a[i];
    108              if (a[i]<0) a[i]+=Mod;
    109         }
    110     }
    111     else   //特征多项式f(x)= (x-1) * (x-r)^(K+1)
    112     {
    113         for (int i=0;i<=K+1;i++)
    114         {
    115              op=(K+1-i)&1? -1:1;
    116              b[i]=1ll*op*C(K+1,i)*Power(r,K+1-i)%Mod;
    117              if (b[i]<0) b[i]+=Mod;
    118         }
    119         for (int i=1;i<K+2;i++) a[i]=b[i-1];
    120         for (int i=0;i<=K+1;i++) 
    121         {
    122             a[i]-=b[i];
    123             a[i]=-a[i];
    124             if (a[i]<0) a[i]+=Mod;
    125         }
    126     }
    127     
    128     K+=2;   //矩阵的规格
    129     
    130     
    131     //预处理M^0...M^(2K-2) 
    132     
    133     memset(M,0,sizeof(M));
    134     
    135     for (int i=0;i<K;i++) M[i].cof[i]=1;
    136     for (int i=0;i<K;i++) M[K].cof[i]=a[i];
    137     
    138     for (int i=K+1;i<=2*K-2;i++)
    139     {
    140         M[i].cof[0]=1ll*a[0]*M[i-1].cof[K-1]%Mod;
    141         for (int j=1;j<K;j++)
    142         {
    143             M[i].cof[j]=1ll*a[j]*M[i-1].cof[K-1]%Mod+M[i-1].cof[j-1];
    144             if (M[i].cof[j]>=Mod) M[i].cof[j]-=Mod;
    145         }
    146     }
    147      
    148     //预处理M^1 M^2 M^4 M^8 M^16... 
    149     R[0]=M[1];
    150     for (int i=1;i<=60;i++) R[i]=R[i-1]*R[i-1];
    151     
    152     S[0]=0;
    153     for (int i=1;i<K;i++) 
    154     {
    155         S[i]=1ll*Power(i,K-2)*Power(r,i)%Mod;
    156         S[i]+=S[i-1];
    157         if (S[i]>=Mod) S[i]-=Mod;
    158     }
    159 }
    160 
    161 
    162 ll Solve()
    163 {
    164     Poly tmp=Poly_Power(n);
    165     
    166     ll ans=0;
    167     
    168     for (int i=0;i<K;i++)
    169     {
    170         ans+=1ll*tmp.cof[i]*S[i]%Mod;
    171         if (ans>=Mod) ans-=Mod;
    172     }
    173     return ans;
    174 }
    175 
    176 int main()
    177 {
    178     //freopen("in.in","r",stdin);
    179     //freopen("out.out","w",stdout);    
    180 
    181     int T; 
    182     while (true)
    183     {
    184         cin >> n >> K >> Mod; r=K;
    185         if (n<0) break;
    186         Init();
    187         printf("%I64d
    ",Solve());
    188     }
    189 
    190     
    191     return 0;
    192 }
    View Code

     

     

    4.codechef Dec Challenge  POWSUMS  https://www.codechef.com/problems/POWSUMS

    官方题解: https://discuss.codechef.com/questions/86250/powsums-editorial

    参考代码:

      1 //https://www.codechef.com/problems/POWSUMS
      2 //https://discuss.codechef.com/questions/86250/powsums-editorial
      3 //https://discuss.codechef.com/questions/49614/linear-recurrence-using-cayley-hamilton-theorem
      4  
      5  
      6 #include <iostream>
      7 #include <cstdio>
      8 #include <cmath>
      9 #include <cstring>
     10 #include <algorithm>
     11 #include <vector>
     12 #include <map>
     13 #include <cstdlib>
     14 #include <set>
     15 using namespace std;
     16  
     17 #define X first
     18 #define Y second
     19 #define Mod 1000000007
     20 #define N 310
     21 typedef long long ll;
     22 typedef pair<int,int> pii;
     23  
     24 inline int Mul(int x,int y){return 1ll*x*y%Mod;}
     25 inline int Add(int x,int y){return ((x+y)%Mod+Mod)%Mod;}
     26  
     27 int n,Q;
     28 int a[N],e[N],inv[N],f[N<<1];
     29  
     30 struct Poly
     31 {
     32     int cof[N];
     33     void print()
     34     {
     35         for (int i=0;i<n;i++) printf("%d ",cof[i]);
     36         printf("
    ");
     37     }
     38 }M[N<<1],tt[63];
     39  
     40 Poly operator * (const Poly &A,const Poly &B)
     41 {
     42     Poly ans; memset(ans.cof,0,sizeof(ans.cof));
     43     
     44     for (int i=0;i<=2*n-2;i++)
     45     {
     46         int res=0;
     47         for (int j=max(0,i-n+1);j<n && j<=i;j++)
     48         {
     49             res+=1ll*A.cof[j]*B.cof[i-j]%Mod;
     50             if (res>=Mod) res-=Mod; 
     51         }
     52         if (i<n) {ans.cof[i]=res;continue;}
     53         for (int j=0;j<n;j++)
     54         {
     55             ans.cof[j]+=1ll*res*M[i].cof[j]%Mod;
     56             if (ans.cof[j]>=Mod) ans.cof[j]-=Mod;
     57         }
     58     }
     59     return ans;
     60 }
     61  
     62 void Init()
     63 {
     64     scanf("%d%d",&n,&Q); 
     65     for (int i=1;i<=n;i++) scanf("%d",&f[i]);
     66  
     67     e[0]=1; 
     68     for (int i=1;i<=n;i++)
     69     {
     70         int op=1; e[i]=0;
     71         for (int j=1;j<=i;j++,op=-op)
     72         {
     73             e[i]+=1ll*op*e[i-j]*f[j]%Mod;
     74             if (e[i]<0) e[i]+=Mod;
     75             if (e[i]>=Mod) e[i]-=Mod;
     76         }
     77         e[i]=1ll*e[i]*inv[i]%Mod;
     78         //cout<<i<<" "<<e[i]<<endl;
     79     }
     80     
     81     for (int i=n+1;i<=2*n-1;i++)
     82     {
     83         f[i]=0;  int op=1;
     84         for (int j=1;j<=n;j++,op=-op)
     85         {
     86             f[i]+=1ll*op*e[j]*f[i-j]%Mod;
     87             if (f[i]>=Mod) f[i]-=Mod;
     88             if (f[i]<0) f[i]+=Mod;
     89         }
     90     }
     91     
     92     for (int i=0;i<n;i++) 
     93         for (int j=0;j<n;j++)
     94             M[i].cof[j]= (i==j);
     95     
     96     //M^n= sum (a[i]*A^i)
     97     for (int i=n-1,op=1;i>=0;i--,op=-op)
     98     {
     99         int tmp=op*e[n-i]; 
    100         if (tmp<0) tmp+=Mod;
    101         M[n].cof[i]=a[i]=tmp;
    102     }
    103          
    104     //Calc linear combination form of   M^(n+1)...M^(2n-2)
    105     for (int i=n+1;i<=2*n-2;i++) 
    106     {
    107         M[i].cof[0]=1ll*a[0]*M[i-1].cof[n-1]%Mod;
    108         for (int j=1;j<n;j++)
    109         {
    110             M[i].cof[j]=1ll*a[j]*M[i-1].cof[n-1]%Mod+M[i-1].cof[j-1];
    111             if (M[i].cof[j]>=Mod) M[i].cof[j]-=Mod;
    112         }
    113         
    114     }
    115     
    116     //预处理出M^2 M^4 M^8... 随机数据可以加速很多  
    117     tt[0]=M[1];
    118     for (int i=1;i<=60;i++) tt[i]=tt[i-1]*tt[i-1];
    119 }
    120  
    121 Poly Poly_Power(ll p)
    122 {
    123     if (p<=2*n-2) return M[p];
    124     Poly res=M[0];
    125     for (int j=60;j>=0;j--) if (p&(1ll<<j)) res=res*tt[j];
    126     return res;
    127 }
    128  
    129 int Solve(ll x)
    130 {
    131     if (x<=2*n-2) return f[x];
    132     
    133     Poly tmp=Poly_Power(x-n);
    134     
    135     int res=0;
    136     for (int i=0;i<n;i++)
    137     {
    138         res+=1ll*tmp.cof[i]*f[n+i]%Mod;
    139         if (res>=Mod) res-=Mod;
    140     }
    141  
    142     if (res<0) res=res%Mod+Mod;
    143     
    144     return res;
    145 }
    146  
    147 int main()
    148 {
    149     //freopen("in.in","r",stdin);
    150     //freopen("out.out","w",stdout);
    151     
    152     //calc_inv
    153     inv[1]=1;
    154     for (int i=2;i<N;i++) inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
    155     int T; ll x; 
    156     
    157     scanf("%d",&T);
    158     while (T--)
    159     {
    160         Init();
    161         while (Q--)
    162         {
    163             scanf("%lld",&x);
    164             printf("%d ",Solve(x));
    165         }
    166         printf("
    ");
    167     }
    168  
    169     return 0;
    170 } 
    View Code

     

  • 相关阅读:
    字符串匹配算法之Rabin-Karp算法
    算法导论之最近顶点对
    php连mssql中文乱码问题
    Trie树
    PAT 1057. Stack (30)
    PAT 1033. To Fill or Not to Fill (25)
    PAT 1034. Head of a Gang (30)
    PAT 1075. PAT Judge (25)
    spring框架资料
    Spring Security资料
  • 原文地址:https://www.cnblogs.com/vb4896/p/6209512.html
Copyright © 2020-2023  润新知