学弟在OJ上加了道“非水斐波那契数列”,求斐波那契第n项对1,000,000,007取模的值,n<=10^15,随便水过后我决定加一道升级版,说是升级版,其实也没什么变化,只不过改成n<=10^30000000,并对给定p取模,0<p<2^31。一样很水嘛大家说对不对。

下面来简单介绍一下BSGS算法,BSGS(Baby steps and giant steps),又称包身工树大步小步法,听上去非常高端,其实就是一个暴力搜索。比如我们有一个方程,a^x≡b (mod c),x是未知数,怎么算?难道是什么神奇的数学公式?直觉和经验告诉我们这玩意儿并不好算,无奈的我们只好枚举x,欧拉dalao告诉我们a^φ(c)≡1 (mod c),再怎么不济,我们O(c)也能算出答案……平时我们常见的模数一般都有十亿左右,明显要T啦,怎么办?

别急,我们来回顾一个经典问题,给你有n个数的集合,问他有多少个子集和为x。(n<=100,000,x<=10^18)

别想了,我也不会做,只好把数据改一改,n<=20?暴力就过啦。n<=50?此时有个常见的做法,我们枚举前n/2个数的和,开个map存下,再枚举后n/2个数,用x减掉枚举出的和再去map里找……复杂度O(2^(n/2))(可能还要多个log)。我们再考虑下子集积?发现并没有区别,枚举前n/2个数的积,存下,再枚举后n/2个数,用x乘上枚举出的积的逆元(假设取模)……是不是想到了什么?我们像快速幂那样把a^x表示成a^1,a^2,a^4,a^8……的积,解那个方程不就是做子集积吗?实际上不用这么麻烦,我们把x表示成只有两位的k进制数,我们就只要枚举两位了,令x=Ak+B,则a^(Ak+B)≡b (mod c),移项得a^Ak≡b*a^-B (mod c),我们先枚举A,算出等式左边,存入map里,再枚举B,算出等式右边,去map里找,就能算出答案啦。要保证这个k进制数只有两位,k自然大约是c^0.5。实际上我们可以稍微把k改大一点(或者A多枚举一点),把x改成x=Ak-B,这样有什么好处呢?你代回去再移一次项就会发现,我们不用算逆元啦!于是我们得到一个较为高效的求解指数方程的算法,复杂度大概是O(c^0.5)(视具体实现可能会多个log)。

好了,我们回到斐波那契数列,10^30000000明显就算我们用某klogklogn的算法也不是很好受(纯属口胡)。实际上很容易想到,斐波那契对一个数取模必然有循环节,因为每一项只和前两项有关,两项数的取值最多p^2种(p为模数),所以循环节至多p^2。事实上,某篇论文证明了,斐波那契数列对p取模的循环节不会超过6p(其实论文中还给出了对于不同的p,循环节的计算方法,不过复杂度应该和接下来要讲的BSGS做法相同(理论上论文做法更优,但我们肯定不想总依靠玄学的论文吧),有兴趣的可以百度一下)。那么我们如何用BSGS算出循环节呢?假设斐波那契数列的递推矩阵为A,我们只要求出A^x≡A (mod p)的一个大于1的解就可以了,求解过程和整数方程并没有太大区别。求出循环节后把那巨长无比的数字串取模下,随便水过该题。

以下是加了一些奇怪的常数优化的代码……如果看的时候遇到一些不知道在干嘛的地方,跳过就好了。

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
#define ll long long
#define MN 30000050
#define K 115000
char s[MN+];
int mod;
struct mat
{
int z[][];
mat(){memset(z,,sizeof(z));}
mat operator*(mat b)
{
mat c;int i,j,k;
for(i=;i<;++i)for(j=;j<;++j)
for(k=;k<;++k)c.z[i][j]=(c.z[i][j]+(ll)z[i][k]*b.z[k][j])%mod;
return c;
}
friend bool operator<(mat a,mat b)
{
for(int i=;i<;++i)for(int j=;j<;++j)
{
if(a.z[i][j]<b.z[i][j])return true;
if(a.z[i][j]>b.z[i][j])return false;
}
}
}f,k,p;
mat pow(ll x)
{
mat r=f,t=f;
for(--x;x;x>>=,t=t*t)if(x&)r=r*t;
return r;
}
map<mat,int> mp;
int main()
{
int i,l,r,mid;ll x,x1,x2,x3,x4,rp;
f.z[][]=f.z[][]=f.z[][]=;
fread(s,,MN,stdin);
for(l=,r=MN;l<=r;)
if(s[mid=l+r>>])l=mid+;
else i=mid,r=mid-;
while(s[--i]<''||s[i]>'')s[i]=;
while(s[i]>=''&&s[i]<='')--i;
for(l=i;s[l]<''||s[l]>'';)s[l--]=;
while(s[++i]>=''&&s[i]<='')mod=(mod<<)+(mod<<)+s[i]-'',s[i]=;
for(p=k=pow(K),i=;i<=K;++i)p=p*k,mp[p]=i;
for(p=f,i=;i<=K;++i,p=p*f)if(x=mp[p]){rp=x*K-i;break;}
for(x1=x2=x3=x4=,i=;s[i];i+=)
x1=(x1*+s[i-]-'')%rp,
x2=(x2*+s[i-]-'')%rp,
x3=(x3*+s[i-]-'')%rp,
x4=(x4*+s[i ]-'')%rp;
x=(x1*+x2*+x3*+x4)%rp;
for(i-=;s[i];++i)x=((x<<)+(x<<)+s[i]-'')%rp;
p.z[][]=p.z[][]=;if(x<)x+=rp;
printf("%d",(p*pow(x-)).z[][]);
}

正常版

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
#define ll long long
#define MN 30000000
#define K 115000
char s[MN+];
int mod;
struct mat
{
int z[][];
mat(){memset(z,,sizeof(z));}
mat operator*(mat b)
{
mat c;int i,j,k;
for(i=;i<;++i)for(j=;j<;++j)
for(k=;k<;++k)c.z[i][j]=(c.z[i][j]+(ll)z[i][k]*b.z[k][j])%mod;
return c;
}
friend bool operator<(mat a,mat b)
{
for(int i=;i<;++i)for(int j=;j<;++j)
{
if(a.z[i][j]<b.z[i][j])return true;
if(a.z[i][j]>b.z[i][j])return false;
}
}
}f,k,p;
mat pow(ll x)
{
mat r=f,t=f;
for(--x;x;x>>=,t=t*t)if(x&)r=r*t;
return r;
}
map<mat,int> mp;
int main()
{
int i;ll x,rp;
f.z[][]=f.z[][]=f.z[][]=;
scanf("%s%d",s,&mod);
for(p=k=pow(K),i=;i<=K;++i)p=p*k,mp[p]=i;
for(p=f,i=;i<=K;++i,p=p*f)if(x=mp[p]){rp=x*K-i;break;}
for(i=x=;s[i];++i)x=((x<<)+(x<<)+s[i]-'')%rp;
p.z[][]=p.z[][]=;if(x<)x+=rp;
printf("%d",(p*pow(x-)).z[][]);
}

[BSGS算法]纯水斐波那契数列的更多相关文章

  1. Reverse反转算法+斐波那契数列递归+Reverse反转单链表算法--C++实现

    Reverse反转算法 #include <iostream> using namespace std; //交换的函数 void replaced(int &a,int & ...

  2. 剑指offer-第二章算法之斐波拉契数列(青蛙跳台阶)

    递归与循环 递归:在一个函数的内部调用这个函数. 本质:把一个问题分解为两个,或者多个小问题(多个小问题相互重叠的部分,会存在重复的计算) 优点:简洁,易于实现. 缺点:时间和空间消耗严重,如果递归调 ...

  3. javascript:算法之斐波那契数列

    一 //1,1,2,3,5,8,13,21这个数列 斐波那契 数列(肥波哪弃) //得到第9项是几? /*******************************111111111递归的思想*** ...

  4. PHP算法之斐波那契数列(递归)

    /*斐波那契数列 源代码分析 f(x) = 1 ; 当 x < 2 ; f(x) = f(x-1)+f(x-2); 当 x >= 2 ; 通项式为:fn ={((1+根号5)/2)^n-( ...

  5. Python算法_斐波那契数列(10)

    写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项.斐波那契数列的定义如下: F(0) = 0,   F(1) = 1F(N) = F(N - 1) + F(N - 2), 其中 ...

  6. 斐波那契数列公式算法-JS实现

    之前算斐波那契数列都是算前两个数相加实现的 比如0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181 ...

  7. 算法 递归 迭代 动态规划 斐波那契数列 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  8. 《BI那点儿事》Microsoft 时序算法——验证神奇的斐波那契数列

    斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10 ...

  9. Java算法求最大最小值,冒泡排序,斐波纳契数列一些经典算法<不断更新中>

    清明在家,无聊,把一些经典的算法总结了一下. 一.求最大,最小值 Scanner input=new Scanner(System.in); int[] a={21,31,4,2,766,345,2, ...

随机推荐

  1. JAVA反射机制基础概念

    反射机制:所谓的反射机制就是java语言在运行时拥有一项自观的能力.通过这种能力可以彻底的了解自身的情况为下一步的动作做准备.下面具体介绍一下java的反射机制.这里你将颠覆原来对java的理解. J ...

  2. 十、Python练习----基础搭建飞机大战

    只是简单的学习了pygame,实现飞机的摧毁还需要多张图片的切换,和sprite(碰撞精灵),还有多种音效的添加(如背景音乐.摧毁特效).以后再深入学习我只是练习一下python. 一.搭建界面(基于 ...

  3. Thinkphp框架部署步骤

    Thinkphp框架部署步骤 thinkphp框架部署起来简单,但是由于步骤较多也容易遗忘: 这是安装了集成环境后的一个www根目录结构: 然后需要在这个目录下面创建一个文件夹做项目:thinkphp ...

  4. clang++ 链接问题 和 VS Code

    clang++ 链接问题 和 VS Code 如果你在windows上使用clang 并且同时安装有vs和mingw, clang链接是会自动使用msvs, 链接时会有LINK error LINK ...

  5. LeetCode & Q53-Maximum Subarray-Easy & 动态规划思路分析

    Array DP Divide and Conquer Description: Find the contiguous subarray within an array (containing at ...

  6. Spring中报"Could not resolve placeholder"的解决方案

    除去properites文件路径错误.拼写错误外,出现"Could not resolve placeholder"很有可能是使用了多个PropertyPlaceholderCon ...

  7. kubernetes入门(06)kubernetes的核心概念(3)

    一.API 对象 API对象是K8s集群中的管理操作单元.K8s集群系统每支持一项新功能,引入一项新技术,一定会新引入对应的API对象,支持对该功能的管理操作.例如副本集Replica Set对应的A ...

  8. Spring Security 入门(1-6-1)Spring Security - 配置文件解析和访问请求处理

    1.在pom.xml中添加maven坐标 <dependency> <groupId>org.springframework.security</groupId> ...

  9. 详解Ajax请求(一)前言——同步请求的原理

    我们知道,ajax是一种异步请求的方式,想要了解异步请求,就必须要先从同步请求说起.常见的同步请求的方式是form表单的提交,我们先从一种同步请求的示例说起. 我们希望输入姓名可以从后台得到身份证号. ...

  10. 判断一个字符串是不是一个合法的IP地址

    最近在笔试的时候遇到碰一道算法题, 要求判断一个字符串是不是合法的ip地址. 将我的思路发出来分享一下,不一定正确,也不一定是最优的方法.希望能分享一些交流 要求用java或者c来实现,我的java代 ...