bzoj 2432: [Noi2011]兔农 (数论+矩阵乘法)
来源:互联网 发布:淘宝微能量运动旗舰店 编辑:程序博客网 时间:2024/05/22 12:57
题目描述
传送门
题目大意:f[1]=f[2]=1;i>2时,f[i]=f[i-1]+f[i-2], if(f[i] % k == 1) f[n]–
求f[n]%p。
题解
一道不错的矩阵乘法的题,感觉自己的数论知识完全跟不上啊。
斐波那契数列?一般都多少有一些规律或者循环节之类的吧。
我们以k=7为例,观察对7取模的Fibonacci数列
以-1得0位置为界,将数列分开,可以发现以下特点。
(1)每段开头必为相同两数,它恰是上一段的最末一位非0数;由于总共只有k−1种余数,所以不超过k段就会出现循环(如果有的话),比如上面k=7时的第2,3,4段就是循环节。
(2)设fibonacci数列的第i项为
如何求解?
(1)根据
(2)根据
(3)由x*f[len-1]得到下一段的开头。
注意事项:
(1)根据
还有一种情况需要考虑,就是不存在
(2)结论:斐波那契数列模k后一定是0,1,1开头的纯循环,而且这个循环节的长度≤6k。有了这个结论我们就可以直接暴力预处理
考虑如何用矩阵加速转移,只要不是每段的最后一个,那么转移都是相同的,可以用矩阵快速幂加速。
对于最后一位我们也可以用转移矩阵表示,每次做完快速幂直接乘上即可。
基本思路已经说完了剩下的就是细节问题了。
(1)比如如果不是循环的就直接矩阵乘法优化。
(2)对于给定的n要先判断是在零散的段中,还是循环节中。如果是零散的段,不会太长,直接暴力合并每一段即可;如果是循环节那么我们将循环节中的段暴力合并然后整体再用快速幂加速,需要特别注意的是矩阵是不满足交换律的,所以乘的时候一定要注意顺序。
(3)因为有一个-1的关系,所以最后要注意(ans%p+p)%p
代码
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define LL long long #define N 1000003using namespace std;LL n,m,p,mod,f[N*6],vis[N],len[N],inv[N],pd[N];struct data{ LL t[4][4]; data(){ memset(t,0,sizeof(t)); }}pr,cp,e,ans,ret[N];data operator*(data a,data b){ data c; for (int i=1;i<=3;i++) for (int j=1;j<=3;j++) for (int k=1;k<=3;k++) c.t[i][j]+=a.t[i][k]*b.t[k][j]%p,c.t[i][j]%=p; return c;}data quickpow(data num,LL x){ data base=num; data ans; for (int i=1;i<=3;i++) ans.t[i][i]=1; while (x) { if(x&1) ans=ans*base; x>>=1; base=base*base; } return ans;}void exgcd(LL a,LL b,LL &x,LL &y){ if (!b) { x=1; y=0; return; } exgcd(b,a%b,x,y); LL t=y; y=x-(a/b)*y; x=t;}LL gcd(LL x,LL y){ LL r; while (y) { r=x%y; x=y; y=r; } return x;}int main(){ freopen("a.in","r",stdin); scanf("%lld%lld%lld",&n,&m,&p); f[1]=f[2]=1; bool flag=false; for (int i=3;;i++){ f[i]=(f[i-1]+f[i-2])%m; if (!vis[f[i]]) vis[f[i]]=i; if (f[i]==1&&f[i-1]==1) break; } pr.t[1][2]=pr.t[2][1]=pr.t[2][2]=pr.t[3][3]=1; for (int i=1;i<=3;i++) cp.t[i][i]=1; cp.t[3][2]=-1; ans.t[1][1]=ans.t[1][3]=1; for (LL t=1;n;) { if (!inv[t]) { if (gcd(t,m)!=1) inv[t]=-1; else { LL x,y; exgcd(t,m,x,y); inv[t]=(x+m)%m; } } if (inv[t]==-1) { ans=ans*quickpow(pr,n); break; } if (!pd[t]||flag){ pd[t]=1; if (!vis[inv[t]]) { ans=ans*quickpow(pr,n); break; } len[t]=vis[inv[t]]; if (n>=len[t]) { n-=len[t]; ret[t]=quickpow(pr,len[t])*cp; ans=ans*ret[t]; t*=f[len[t]-1]%m; t%=m; } else ans=ans*quickpow(pr,n),n=0; } else { data now; LL cnt=0; for (int i=1;i<=3;i++) now.t[i][i]=1; for (LL i=t*f[len[t]-1]%m;i!=t;(i*=f[len[i]-1])%=m) now=now*ret[i],cnt+=len[i]; now=ret[t]*now; cnt+=len[t]; ans=ans*quickpow(now,n/cnt); n%=cnt; flag=true; } } printf("%lld\n",(ans.t[1][2]%p+p)%p);}
- bzoj 2432: [Noi2011]兔农 (数论+矩阵乘法)
- 【数论】【矩阵乘法】【NOI2011】兔农
- bzoj 2432 [Noi2011]兔农 [矩阵]
- bzoj 3328: PYXFIB 数论&矩阵乘法
- [NOI2011]兔农(斐波那契数列+乘法逆+矩阵加速)
- [数论][二项式定理][矩阵乘法] BZOJ 3328: PYXFIB
- HDU 2971(数论,构造矩阵+矩阵乘法优化)
- HDU 2855(数论,构造矩阵+矩阵乘法)
- bzoj 1009(KMP+矩阵乘法)
- 【BZOJ 2738】 矩阵乘法
- [BZOJ 2738]矩阵乘法
- BZOJ 1297 矩阵乘法
- BZOJ 2738: 矩阵乘法
- BZOJ 2738: 矩阵乘法
- bzoj 2738 矩阵乘法
- 【矩阵快速幂】[NOI2011]兔农
- 矩阵乘+坑 [NOI2011] 兔农
- bzoj2432: [Noi2011]兔农 快速幂+数论
- 深度增强学习David Silver(一)——介绍
- maven阿里云中央仓库
- leetcode 8
- 《google软件测试之道》读书笔记
- Category添加weak属性的精简版本
- bzoj 2432: [Noi2011]兔农 (数论+矩阵乘法)
- JFinal
- 旋转矩阵与四元数
- 实现三个并排div,两边固定宽度,中间自适应的四个方法
- tcpdump命令
- hibernate的方法运用
- 566. Reshape the Matrix
- dubbo Filter源码分析
- VGA、DVI、HDMI哪个好?三种视频信号接口有什么区别?