• 「WC2021」斐波那契


    题目

    点这里看题目。

    分析

    有点厉害啊......

    首先注意到,由于递推关系和询问参数 \(a,b\) 没有关系,我们可以使用斐波那契数列 \(f\) 来给出 \(F\) 的“通项”。换言之,也就是:

    \[F_n=af_n+(b-a)f_{n-1} \]

    Note.

    简单的理解方法:考虑转移矩阵的幂次 \(\begin{bmatrix}1&1\\1&0\end{bmatrix}^n=\begin{bmatrix}f_n&f_{n-1}\\ f_{n-1}&f_{n-2}\end{bmatrix}\)

    这里,我们允许 \(f\) 出现负下标。负下标的情况可以由递推式给出:

    \[f_{n-2}=f_{n}-f_{n-1},n\le 1 \]

    比如,有 \(f_{-1}=0,f_{-2}=1\)

    去掉一些容易处理的边界,我们现在考虑 \(a,b,b-a\not\equiv 0\pmod m\) 的情况。所以,我们只需要解决:

    \[uf_n\equiv vf_{n-1}\pmod m \]

    先做一点显然的处理:对于 \(u,v,m\) 同时除掉 \(\gcd(u,v,m)\),得到 \(u',v',m'\)

    如果有着良好的互质性,比如 \(m'\) 是一个质数,那么接下来我们可以直接将 \(f\)\(u,v\) 分开,从而做一些预处理。不过,虽然 \(\gcd(u',v',m')=1\),我们却还无法保证 \(u'\perp m'\) 或者 \(v'\perp m'\),因此还需要再做尝试。

    很容易想到把 \(\gcd(u',m')\)\(\gcd(v',m')\) 给除掉。但是怎么保证除了之后不会出现分数?结合我们的目的和斐波那契自身的性质 \(f_{n-1}\perp f_n\),我们不难猜到这样一个结论:

    \[\begin{aligned} \gcd(u',m')&=\gcd(f_{n-1},m')=d_2\\ \gcd(v',m')&=\gcd(f_n,m')=d_1 \end{aligned} \]

    为了节约篇幅,证明缩在了这里:

    Proof.

    刚拿到这个条件的时候,我们注意到:同余条件可以产生 \(\gcd\),而反过来比较困难。因此,我们选择将仅有的同余式转化成 \(\gcd\) 的关系:

    \[\gcd(u'f_n,m')=\gcd(v'f_{n-1},m')=a \]

    \(a\) 除到 \(\gcd\) 里面去,并考虑 \(a\) 是如何“分配”而来的。也就是说:

    \[\begin{aligned} &\gcd\left(\frac{u'd_1}{a}\cdot \frac{f_n}{d_1},\frac{m'}{a}\right)\\ =&\gcd\left(\frac{v'd_2}{a}\cdot \frac{f_{n-1}}{d_2},\frac{m'}{a}\right)\\ =&1 \end{aligned} \]

    这将会给出:

    \[\gcd(d_1u',d_2v',m')=a \]

    由于 \(f_{n-1}\perp f_n\),显然有 \(d_1\perp d_2\)。结合 \(\gcd(u',v',m')=1\),我们可以知道 \(d_1d_2=a\)。此时我们已经完成了 \(d_2|\gcd(u',m')\)\(d_1|\gcd(v',m')\)

    接下来证明 \(\gcd(u',m')=d_2\)。这一点只需要结合 \(\gcd(u'f_n,m')=d_1d_2,d_1\perp d_2\) 即可得到。证明 \(\gcd(v',m')=d_1\) 类似。

    所以,这样处理过后我们又将 \(u,v\)\(f\) 分开了。对于每个 \(m'|m\) 我们可以处理出所有可能的三元组 \((d_1,d_2,(f_nd_1^{-1})(f_{n-1}d_2^{-1})^{-1}\bmod (m'd_1^{-1}d_2^{-1}))\)。由于斐波那契的循环节长度是 \(O(m')\),因此对于每个质因子暴力枚举即可。询问直接查一下就结束了。

    时间复杂度为 \(O(\sigma (m)\log m+n\log m)\)

    代码

    由于我偷懒没有精细实现,这份代码在洛谷上会 TLE,在 LOJ 上才能过

    #include <map>
    #include <set>
    #include <cstdio>
    #include <vector>
    #include <iostream>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    const int MAXN = 1e6 + 5, MAXD = 2e3 + 5;
    
    template<typename _T>
    void read( _T &x ) {
    	x = 0; char s = getchar(); bool f = false;
    	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    	if( f ) x = -x;
    }
    
    template<typename _T>
    void write( _T x ) {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    struct Triple {
    	int a, b, c;
    
    	Triple(): a( -1 ), b( -1 ), c( -1 ) {}
    	Triple( int A, int B, int C ): a( A ), b( B ), c( C ) {}
    
    	inline bool operator < ( const Triple &q ) const {
    		return a == q.a ? ( b == q.b ? c < q.c : b < q.b ) : a < q.a;
    	}
    };
    
    std :: set<std :: pair<int, int> > ever;
    std :: map<Triple, int> occ[MAXD];
    
    int dvs[MAXD], tot = 0;
    
    int N, M;
    
    inline int Gcd( int x, int y ) { for( int z ; y ; z = x, x = y, y = z % y ); return x; }
    
    inline int Exgcd( const int &a, const int &b, int &x, int &y ) {
    	if( ! b ) return x = 1, y = 0, a;
    	int d = Exgcd( b, a % b, y, x );
    	y -= x * ( a / b ); return d;
    }
    
    inline int Inv( const int &a, const int &mod ) {
    	static int x, y;
    	Exgcd( a, mod, x, y );
    	return ( x % mod + mod ) % mod;
    }
    
    void Enumerate( const int &mod ) {
    	ever.clear();
    	dvs[++ tot] = mod;
    	int x = 1, y = 0, p, q;
    	for( int n = 0 ; ; n ++ ) {
    		if( ever.find( { x, y } ) != ever.end() ) break;
    		ever.insert( { x, y } );
    		p = Gcd( x, mod ), q = Gcd( y, mod );
    		Triple nxt( p, q, 1ll * ( y / q ) * Inv( x / p, mod / p / q ) % ( mod / p / q ) );
    		if( occ[tot].find( nxt ) == occ[tot].end() )
    			occ[tot][nxt] = n;
    		y = ( x + y ) % mod, std :: swap( x, y );
    	}
    }
    
    int main() {
    	read( N ), read( M );
    	rep( i, 1, M )
    		if( M % i == 0 )
    			Enumerate( i );
    	while( N -- ) {
    		int a, b, m = M;
    		read( a ), read( b );
    		if( a == 0 ) { 
    			puts( "0" ); 
    			continue; 
    		}
    		if( b == 0 ) {
    			puts( "1" );
    			continue;
    		}
    		b = ( a - b + m ) % m;
    		int d = Gcd( Gcd( a, b ), m );
    		a /= d, b /= d, m /= d;
    		int d1 = Gcd( a, m ), d2 = Gcd( b, m );
    		int idx = std :: lower_bound( dvs + 1, dvs + 1 + tot, m ) - dvs;
    		Triple qry( d2, d1, 1ll * ( a / d1 ) * Inv( b / d2, m / d1 / d2 ) % ( m / d1 / d2 ) );
    		if( occ[idx].find( qry ) == occ[idx].end() ) 
    			puts( "-1" );
    		else
    			write( occ[idx][qry] ), putchar( '\n' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    【转】jar参数运行应用时classpath的设置方法
    【转】命令行执行main方法
    【转】JAVA下的多线程程序造成系统时钟变快
    Spring MVC学习总结(6)——一些Spring MVC的使用技巧
    Spring MVC学习总结(6)——一些Spring MVC的使用技巧
    Maven学习总结(21)——Maven常用的几个核心概念
    专访李智慧:架构是最高层次的规划和难以改变的决定
    华为架构师8年经验谈:从单体架构到微服务的服务化演进之路
    华为架构师8年经验谈:从单体架构到微服务的服务化演进之路
    Spring学习总结(12)——Druid连接池及监控在spring配置
  • 原文地址:https://www.cnblogs.com/crashed/p/16439193.html
Copyright © 2020-2023  润新知