BZOJ4666: 小Z的胡话【斐波那契循环节】

题目描述:

在这里插入图片描述

题目分析:

对于一个longlong级别的斐波那契数列我们能做什么呢?
naive一点就只能算一算模mod下的第n项了。
如果这个mod很小,那么我们暴力枚举就可以得到答案。
如果x%Mod=a%Mod, 那么对于一个比Mod小的mod,x%mod也一定等于a%mod
也就是说我们可以先取一个小mod,把x%mod=a%mod的x找出来放进vector里面,再考虑大Mod的情况。
但是x显然有无穷个,于是我们打一打表会惊讶然后恍然大悟斐波那契数列模一个数是有周期的。
于是我们只需要把第一个周期中的x找出来,为了方便,我们让mod每次乘10,新mod下的x一定是原mod下的x+几倍周期得到的,同样我们只需要找新mod的第一个周期内的x

mod pk的周期按道理来说应该是mod p周期的整数倍,打表也可以验证,所以判断是否达到新周期只需要不断地往后推上一次的周期,判断是否回到原点即可。

一轮的复杂度就是 O ( c n t ∗ T m o d ∗ 10 / T m o d ) O(cnt*T_{mod*10}/T_{mod}) O(cntTmod10/Tmod),cnt是很少的,而 T m o d T_{mod} Tmod大约是mod的1.5倍,所以是很快的。

至于斐波那契为什么有周期可以参考这篇论文然而我看不下去。。 ),网上也有很多关于循环节的题目。

Code:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL mod,a,L,L2;
struct Mat{
	LL s[2][2];
	Mat(){memset(s,0,sizeof s);}
	void init(LL k){
		s[0][0]=1,s[0][1]=0,s[1][0]=0,s[1][1]=1;
		Mat b;b.s[0][0]=0,b.s[0][1]=b.s[1][0]=b.s[1][1]=1;
		for(;k;k>>=1,b=b*b) if(k&1) *this=*this*b;
	}
	/*u64 Mul(u64 a,u64 b) {
		if (mod<=1000000000)
			return a*b%mod;
		return b?((Mul(a,b>>16)<<16)+a*(b&65535))%mod:0;
	}*/
	inline LL mul(LL a,LL b){return (a*b-(long long)((long double)a*b/mod)*mod+mod)%mod;}
	inline void add(LL &x,LL y){x+=y+(x+y>=mod?-mod:0);}
	Mat operator * (const Mat &B){
		Mat ret;
		for(int k=0;k<2;k++)
			for(int i=0;i<2;i++) if(s[i][k])
				for(int j=0;j<2;j++) if(B.s[k][j])
					add(ret.s[i][j],mul(s[i][k],B.s[k][j]));
		return ret;
	}
}f,g,t;
vector<LL>ans,ans2;
int main()
{
	cin>>a;
	ans.push_back(0),mod=L=1;
	for(int p=1;p<=13;p++){
		mod*=10,L2=0;
		g.init(L),f.init(0);
		do{
			for(LL i:ans){
				t.init(i+L2);
				if(t.s[1][0]==a%mod) ans2.push_back(i+L2);	
			}
			f=f*g,L2+=L;
		}while(f.s[0][0]!=1||f.s[0][1]!=0||f.s[1][0]!=0||f.s[1][1]!=1);
		L=L2,ans.swap(ans2),ans2.clear();
	}
	if(ans.empty()) puts("-1");
	else cout<<ans[0]<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值