[斐波那契循环节 数学技巧] HDU 3977 Evil teacher
2019-04-13 15:58发布
生成海报
找到了两种做法 不同的关键在于对于一个质数p的循环节的不同求法
法一:
来自http://blog.csdn.net/prime7/article/details/11017111
分析过程:首先我们知道fib数列模p如果出现了连续的1,0就意味这着开始循环了,因为接下来的项就是1 1 2 3 5等等。
那么很显然如果在第k位第一次出现了1,0,那么对于以后的1,0都可以表示为k*m。
那么,现在我们考虑如果fib数列模p在第pos位第一次出现了0,那么设0前面的那个数为a,则接下来的序列将是a,0,a,
a,2a,3a,5a,8a,....。可以看出a的系数就是一个fib数列,那么我们就可以得到fib(k+i)%p=a*fib(i)%p,其中i满
足0
那么我们现在先来说说如何求fib数模一个正整数n的循环节长度:
对于这个问题,我们先对n进行素因子分解,得到:,然后先对每一个形如p^k的数计算循环节,然后它们
的最小公倍数就是n的循环节长度(当然这里面涉及到CRT等等方面的基础)。那么现在问题就是计算p^k的循环节,这个问题
可以进一步简化成求G(p)*p^(k-1)。其中G(p)表示fib数列模素数p的循环节长度,所以现在的问题是如何求fib数列模一个
小于10^6的素数p的循环节长度。
求fib数列模p(p是素数)的最小循环节方法:
暴力枚举fib[i]%p==0的最小的i,然后记录pos=i+1,设a为fib[i]%p==0的前一位数,即a=fib[i-1]
那么我们知道fib数列模p的最小循环节长度一定是pos*x,那么也就是说现在要求一个最小的数x,满足,
求出x后,那么问题就解决了,剩下的就是合并等等。
#include
#include
#include
using namespace std;
typedef long long ll;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline ll Gcd(ll a,ll b){
return b?Gcd(b,a%b):a;
}
inline ll Lcm(ll a,ll b){
return a*b/Gcd(a,b);
}
inline ll Pow(ll a,ll b,ll p){
ll ret=1;
for (;b;b>>=1,a=a*a%p)
if (b&1)
ret=ret*a%p;
return ret;
}
const int maxn=1000000;
int prime[maxn+5],num;
int vst[maxn+5];
inline void Pre(){
for (int i=2;i<=maxn;i++){
if (!vst[i]) prime[++num]=i;
for (int j=1;j<=num && (ll)prime[j]*i<=maxn;j++){
vst[prime[j]*i]=1;
if (i%prime[j]==0)
break;
}
}
}
inline ll calc(ll p){
ll a=3,f1=1,f2=1,f3=2%p;
while (f3) f1=f2,f2=f3,f3=(f1+f2)%p,a++;
ll ret=p-1;
for (int i=1;(ll)i*i<=p-1;i++)
if ((p-1)%i==0){
if (Pow(f2,i,p)==1) ret=min(ret,(ll)i);
if (Pow(f2,(p-1)/i,p)==1) ret=min(ret,(ll)(p-1)/i);
}
return ret*a;
}
inline ll Solve(ll n){
int tem=n; ll ret=0;
for (int i=1;prime[i]<=tem;i++)
if (tem%prime[i]==0){
ll tmp=1;
while (tem%prime[i]==0) tem/=prime[i],tmp*=prime[i];
tmp=tmp/prime[i]*calc(prime[i]);
ret=!ret?tmp:Lcm(ret,tmp);
}
return ret;
}
int main(){
int T,n,ca=0;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(T); Pre();
while (T--){
read(n);
if (n==1)
printf("Case #%d: %I64d
",++ca,1LL);
else
printf("Case #%d: %I64d
",++ca,Solve(n));
}
return 0;
}
法二:
来自:http://blog.csdn.net/acdreamers/article/details/10983813
对于一个正整数n,我们求Fib数模n的循环节的长度的方法如下:
(1)把n素因子分解,即
(2)分别计算Fib数模每个的循环节长度,假设长度分别是
(3)那么Fib模n的循环节长度
从上面三个步骤看来,貌似最困难的是第二步,那么我们如何求Fib模的循环节长度呢?
这里有一个优美的定理:Fib数模的最小循环节长度等于,其中表示Fib数模素数的最小循环节长度。可以看出我们现在最重要的就是求
对于求我们利用如下定理:
如果5是模的二次剩余,那么循环节的的长度是的因子,否则,循环节的长度是的因子。
顺便说一句,对于小于等于5的素数,我们直接特殊判断,loop(2)=3,loop(3)=8,loop(5)=20。
那么我们可以先求出所有的因子,然后用矩阵快速幂来一个一个判断,这样时间复杂度不会很大。
#include
#include
#include
using namespace std;
typedef long long ll;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
namespace F{
struct Matrix{
ll a,b,c,d;
Matrix(ll a=0,ll b=0,ll c=0,ll d=0):a(a),b(b),c(c),d(d) { }
};
inline Matrix Mul(Matrix A,Matrix B,ll p){
return Matrix((A.a*B.a+A.b*B.c)%p,(A.a*B.b+A.b*B.d)%p,(A.c*B.a+A.d*B.c)%p,(A.c*B.b+A.d*B.d)%p);
}
inline Matrix Pow(Matrix a,ll b,ll p){
Matrix ret(1,0,0,1);
for (;b;b>>=1,a=Mul(a,a,p))
if (b&1)
ret=Mul(ret,a,p);
return ret;
}
inline ll f(ll n,ll p){
if (!n) return 0;
return Pow(Matrix(1,1,1,0),n-1,p).a;
}
}
inline ll Gcd(ll a,ll b){
return b?Gcd(b,a%b):a;
}
inline ll Lcm(ll a,ll b){
return a*b/Gcd(a,b);
}
inline ll Pow(ll a,ll b,ll p){
ll ret=1;
for (;b;b>>=1,a=a*a%p)
if (b&1)
ret=ret*a%p;
return ret;
}
const int maxn=1000000;
int prime[maxn+5],num;
int vst[maxn+5];
inline void Pre(){
for (int i=2;i<=maxn;i++){
if (!vst[i]) prime[++num]=i;
for (int j=1;j<=num && (ll)prime[j]*i<=maxn;j++){
vst[prime[j]*i]=1;
if (i%prime[j]==0)
break;
}
}
}
int d[1005],cnt;
int pri[25],q[25],len;
void dfs(ll t,ll cur){
if(t==len+1) { d[++cnt]=cur; return; }
dfs(t+1,cur);
for (int i=1;i<=q[t];i++)
dfs(t+1,cur*=pri[t]);
}
inline void Mac(int n){
int tem=n; len=0;
for (int i=1;i<=num && (ll)prime[i]*prime[i]<=tem;i++)
if (tem%prime[i]==0){
pri[++len]=prime[i]; q[len]=0;
while (tem%prime[i]==0) tem/=prime[i],q[len]++;
}
if (tem!=1) pri[++len]=tem,q[len]=1;
cnt=0;
dfs(1,1);
}
inline ll legendre(ll a,ll p){
if(Pow(a,(p-1)>>1,p)==1) return 1; return -1;
}
const ll ans[]={0,0,3,8,0,20};
inline ll calc(ll p){
if (p<=5) return ans[p];
ll n;
if (legendre(5,p)==1) Mac(p-1); else Mac((p+1)<<1);
sort(d+1,d+cnt+1);
for (int i=1;i<=cnt;i++){
if (F::f(d[i],p)==0 && F::f(d[i]+1,p)==1)
return d[i];
}
}
inline ll Solve(ll n){
int tem=n; ll ret=0;
for (int i=1;prime[i]<=tem;i++)
if (tem%prime[i]==0){
ll tmp=1;
while (tem%prime[i]==0) tem/=prime[i],tmp*=prime[i];
tmp=tmp/prime[i]*calc(prime[i]);
ret=!ret?tmp:Lcm(ret,tmp);
}
return ret;
}
int main(){
int T,n,ca=0;
freopen("t.in","r",stdin);
freopen("t2.out","w",stdout);
read(T); Pre();
while (T--){
read(n);
if (n==1)
printf("Case #%d: %I64d
",++ca,1LL);
else
printf("Case #%d: %I64d
",++ca,Solve(n));
}
return 0;
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮