zoukankan      html  css  js  c++  java
  • 【洛谷7325】[WC2021] 斐波那契(数论)

    点此看题面

    • 定义(F_0=a,F_1=b,F_i=(F_{i-1}+F_{i-2}) mod m)
    • 初始给定(m),每次询问给出(a,b),求最小的(n)满足(F_n=0)
    • (n,mle10^5)

    斐波那契数

    首先我们发现(F_i=aFib_{i-2}+bFib_{i-1})(可以归纳证明)。

    那么现在就是要找到最小的(n)使得(aFib_{n-2}+bFib_{n-1}equiv0(mod m))

    方便起见令把原本的(b)变成(-b),那么就得到(aFib_{n-2}equiv bFib_{n-1}(mod m))

    如果(m)是质数,可以得到(frac abequivfrac{Fib_{n-1}}{Fib_{n-2}}(mod m)),直接预处理出(p_x)表示(frac{Fib_{n-1}}{Fib_{n-2}}equiv x(mod m))的最小的(n),询问时查询(p_{frac ab})即可。

    可惜(m)不一定是质数,但此时我们需要注意一个关键性质:(Fib_{n-1}perp Fib_{n-2})

    证明就是模拟辗转相减法的过程,(gcd(Fib_{n-1},Fib_{n-2})=gcd(Fib_{n-2},Fib_{n-3})=...=gcd(1,0)=1)

    因此,我们首先约去(gcd(a,b,m)),得到(a'Fib_{n-2}equiv b'Fib_{n-1}(mod m'))

    由于模(m)意义下两式相等,必须满足(gcd(a'Fib_{n-2},m')=gcd(b'Fib_{n-1},m'))

    (p=gcd(a',m'),q=gcd(b',m')),因为已经约去过(gcd(a,b,m)),显然有(pperp q)

    因此,我们得到(gcd(Fib_{n-1},m')=p,gcd(Fib_{n-2},m')=q)

    在两式中同时约去(pq)得到(a''Fib_{n-2}'equiv b''Fib_{n-1}'(mod {frac{m'}{pq}}))

    此时由于所有数都与模数互质,可以套用先前质数的做法,得到(frac{a''}{b''}equivfrac{Fib_{n-1}'}{Fib_{n-2}'}(mod frac{m'}{pq}))

    所以只要对于每个(m')的每个三元组((x,y,v))记录满足(gcd(Fib_{n-2},m)=x)(gcd(Fib_{n-1})=y)(frac{Fib_{n-1}div y}{Fib_{n-2}div x}equiv v(mod frac{m'}{xy}))的最小的(n)即可。

    由于斐波那契数在模(m)意义下的循环节是(O(m))的,直接暴力枚举直至产生循环为止即可。

    代码:(O(sigma(m)logm))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    using namespace std;
    int m;I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
    I void exgcd(CI x,CI y,int& a,int& b) {y?(exgcd(y,x%y,b,a),b-=x/y*a):(a=1,b=0);}
    I int Inv(CI x,CI y) {RI a,b;return exgcd(x,y,a,b),(a%y+y)%y;}
    struct Data {int gx,gy,v;I Data(CI x=0,CI y=0,CI z=0):gx(x),gy(y),v(z){}
    	I bool operator < (Con Data& o) Con {return gx^o.gx?gx<o.gx:(gy^o.gy?gy<o.gy:v<o.v);}};map<Data,int> p[N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    	I void NA() {pc('-'),pc('1'),pc('
    ');}
    }using namespace FastIO;
    int main()
    {
    	RI Qt,i,j,x,y,z,gx,gy,v,m_;for(read(Qt,m),i=2;i<=m;++i) if(!(m%i)) for(j=2,x=y=1;;++j)//对于m的每个因数暴力预处理
    	{
    		m_=i/(gx=gcd(x,i))/(gy=gcd(y,i)),v=1LL*(y/gy)*Inv(x/gx,m_)%m_,!p[i].count(Data(gx,gy,v))&&(p[i][Data(gx,gy,v)]=j);//记录每个三元组最早的n
    		if(z=(x+y)%i,x=y,y=z,x==1&&y==1) break;//如果产生循环节
    	}
    	RI g;W(Qt--)
    	{
    		if(read(x,y),!x) {writeln(0);continue;}if(!y) {writeln(1);continue;}y=(m-y)%m;//特判x=0或y=0,将y变成-y
    		g=gcd(gcd(x,y),m),m_=m/g/(gx=gcd(x/=g,m/g))/(gy=gcd(y/=g,m/g)),v=1LL*(x/gx)*Inv(y/gy,m_)%m_;//计算出三元组
    		p[m/g].count(Data(gy,gx,v))?writeln(p[m/g][Data(gy,gx,v)]):NA();//如果存在就输出,否则无解
    	}return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    AngularJS 简介
    Java基础知识学习(九)
    Java基础知识学习(八)
    算法(二)
    Java基础知识学习(七)
    Java基础知识学习(六)
    Java基础知识学习(五)
    Java基础知识学习(四)
    Java基础知识学习(三)
    Java基础知识学习(二)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu7325.html
Copyright © 2011-2022 走看看