[BSGS算法]纯水斐波那契数列
学弟在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算法]纯水斐波那契数列的更多相关文章
- Reverse反转算法+斐波那契数列递归+Reverse反转单链表算法--C++实现
Reverse反转算法 #include <iostream> using namespace std; //交换的函数 void replaced(int &a,int & ...
- 剑指offer-第二章算法之斐波拉契数列(青蛙跳台阶)
递归与循环 递归:在一个函数的内部调用这个函数. 本质:把一个问题分解为两个,或者多个小问题(多个小问题相互重叠的部分,会存在重复的计算) 优点:简洁,易于实现. 缺点:时间和空间消耗严重,如果递归调 ...
- javascript:算法之斐波那契数列
一 //1,1,2,3,5,8,13,21这个数列 斐波那契 数列(肥波哪弃) //得到第9项是几? /*******************************111111111递归的思想*** ...
- PHP算法之斐波那契数列(递归)
/*斐波那契数列 源代码分析 f(x) = 1 ; 当 x < 2 ; f(x) = f(x-1)+f(x-2); 当 x >= 2 ; 通项式为:fn ={((1+根号5)/2)^n-( ...
- Python算法_斐波那契数列(10)
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项.斐波那契数列的定义如下: F(0) = 0, F(1) = 1F(N) = F(N - 1) + F(N - 2), 其中 ...
- 斐波那契数列公式算法-JS实现
之前算斐波那契数列都是算前两个数相加实现的 比如0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181 ...
- 算法 递归 迭代 动态规划 斐波那契数列 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 《BI那点儿事》Microsoft 时序算法——验证神奇的斐波那契数列
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10 ...
- Java算法求最大最小值,冒泡排序,斐波纳契数列一些经典算法<不断更新中>
清明在家,无聊,把一些经典的算法总结了一下. 一.求最大,最小值 Scanner input=new Scanner(System.in); int[] a={21,31,4,2,766,345,2, ...
随机推荐
- Papers3
Papers3 总览 Papers功能主要是文献收集,整理,阅读和引用. 主页面: 文献收集 Papers提供两种导入文献的方法:在线搜索和本地导入: 在线搜索 可以通过搜索题目,作者,摘要等内容中的 ...
- C简单实现双向链表
<pre name="code" class="cpp">//链表结构 typedef struct DulNode { DataType data ...
- .NET Core装饰模式和.NET Core的Stream
该文章综合了几本书的内容. 某咖啡店项目的解决方案 某咖啡店供应咖啡, 客户买咖啡的时候可以添加若干调味料, 最后要求算出总价钱. Beverage是所有咖啡饮料的抽象类, 里面的cost方法是抽象的 ...
- 微信支付 chooseWXPay:fail
本来以为解决了微信支付get_brand_wcpay_request:faill这个问题后就万事大吉了,结果又迈入了另一个坑... 问题原因: 1.生成签名的时间戳参数名timestamp的s大小写问 ...
- Ubuntu Desktop 16.04 LTS 下成功配置Jupyter的两个python内核版本(2.7x,3.5x)
Ubuntu Desktop 16.04 LTS 安装好系统默认就有python两个不同版本(2.7.12和3.5.2) 现在来熟悉一下jupyter的对python这两个不同python版本的内核 ...
- 关于PHP7
目前一直使用php7也看了许多文档视频等,整理一下相关细节(仅为记录-),对于PHP7性能,如下图所示. * 在wordpress3.0.1中 php7比php5.6性能提升约3倍左右 新特性 一.变 ...
- Python基础数据类型之集合以及其他和深浅copy
一.基础数据类型汇总补充 list 在循环一个列表时,最好不要删除列表中的元素,这样会使索引发生改变,从而报错(可以从后向前循环删除,这样不会改变未删元素的索引). 错误示范: lis = [,,, ...
- centos7配置Apache支持HTTPS
Apache版本2.4 安装mod_ssl yum install mod_ssl 建立文件夹,存放sslkey mkdir /etc/httpd/ssl/ 建立凭证档 openssl req -x5 ...
- uvalive 5834 Genghis Khan The Conqueror
题意: 给出一个图,边是有向的,现在给出一些边的变化的信息(权值大于原本的),问经过这些变换后,MST总权值的期望,假设每次变换的概率是相等的. 思路: 每次变换的概率相等,那么就是求算术平均. 首先 ...
- SpringBoot(六):springboot热部署
在j2ee项目开发中,热部署插件是JRebel.JRebel的使用为开发人员带来了极大的帮助,且挺高了开发便捷.而在SpringBoot开发生态环境中,SpringBoot热部署常用插件是:sprin ...