递推关系( Recurrences, UVa 10870)(矩阵快速幂)

来源:互联网 发布:淘宝写论文查重靠谱吗 编辑:程序博客网 时间:2024/06/15 22:26
递推关系( Recurrences, UVa 10870
考虑线性递推关系
fn) =a
1fn1) +a2fn2) +a3fn3
a
dfnd) , 最著名的例子是Fibonacci数列f1) =f2) =1
fn) =fn1) +fn2) , 因此d2a
1a21。 你的任务是计算
fn) 除以m的余数。
【 输入格式】
输入包含若干组数据。 每组数据第一行为
3个整数dn
m1≤d≤151≤n≤23111≤m≤46340) 。 第二行为d个非负整数a
1a2
a
d。 第三行为d个非负整数f1) , f2) , fd) 。 这些非负整
数均不超过
2321。 输入结束标志为dnm0
【 输出格式】
对于每组数据, 输出
fn) 除以m的余数。
【 分析】
线性递推关系是组合计数中很常见的一种递推关系。 直接利用递推
式, 需要
Ond) 时间才能算出fn) , 时间无法承受。
现在已经有了
fn) =a
1fn1) +a2fn2) +a3fn3) +
a
dfnd) , 再加上fn1) =fn1) ,fn2) =fn2) 等显然

成立的式子, 可以得到如下关系式


其中A称为相伴矩阵, 也称友矩阵[14]companion matrix) 或者Q
阵。 这正是
把一个向量变成另一个向量。 根据矩阵乘法的定义,Fn
AndFd) 。 利用快速矩阵幂, 本题的总时间复杂度为Od3logn) 。
程序留给读者编写。 唯一需要说明的是矩阵幂的写法。 在数论中,
幂取模都是用递归写法, 但这里推荐用迭代写法, 因为矩阵的数据量较
大, 如果递归求幂, 一不注意就会占用更多的空间, 相比之下迭代写法

更自然。

//矩阵快速幂 //和题解的差不多,但是友矩阵不是一样的//注意点就是要防溢出 #include<cstdio>#include<iostream>#include<cstring>using namespace std;long long d[20][20],dd,n,m,ans[20];void work(){n--;long long t[20],tt[20][20];while(n){if(n&1){memcpy(t,ans,sizeof(t));memset(ans,0,sizeof(ans));for(int i=1;i<=dd;i++){for(int j=1;j<=dd;j++)ans[i]=(ans[i]+d[i][j]*t[j])%m;}}n>>=1;memcpy(tt,d,sizeof(tt));memset(d,0,sizeof(d));for(int i=1;i<=dd;i++)for(int j=1;j<=dd;j++){for(int k=1;k<=dd;k++)d[i][j]+=tt[i][k]*tt[k][j];d[i][j]%=m;}}}int main(){while(~scanf("%lld%lld%lld",&dd,&n,&m)&&(dd||n||m)){memset(d,0,sizeof(d));for(int i=1;i<=dd;i++)scanf("%lld",&d[dd][dd-i+1]);int x=1,y=2;while(x<dd) d[x++][y++]=1;//友矩阵已建好for(int i=1;i<=dd;i++)scanf("%lld",ans+i);work();printf("%lld\n",ans[1]);}return 0;}



原创粉丝点击