题解 P7325
前言
数学符号约定
- \(a,b,p\):表示任意自然数。
- \(F_x\):表示广义斐波那契数列的第 \(x\) 项。
- \(f_x\):表示普通斐波那契数列的第 \(x\) 项.
如非特殊说明,将会按照上述约定书写符号。
题目分析
首先引入一条定理:
普通斐波那契数列在模 \(m\) 意义下纯循环,且循环节为 \(O(m)\)。
证明可以去看 wlzhouzhuan 的博客,碍于篇幅限制不多赘述。
然后我们定义 \(F_0 = a,\, F_1 = b\),则显然我们有:\(F_p = af_{p-1} + bf_{p}\),证明平凡,如果想不出来可以手玩一下试试。
此时,我们的目标是对于 \(a,b\) 求出 \(p\) 使其满足下式:
移项,得:
此时,我们不妨令 \(t = -b\),则我们有:
观察上式,发现如果我们能将 \(f_{p-1}\) 和 \(t\) 分别移项,然后预处理 \(\dfrac {f_p}{f_{p-1}}\) 问题就十分的简单了,但是因为 \(m\) 不为质数(即两者不一定互质),故我们不能这么做。
然而,俗话说的好,没有条件就让我们创造条件,先考虑对原式化简,设 \(d = \gcd(a,t,m)\),则原式变为:
如果我们想让 \(\dfrac td\) 在 \(\dfrac md\) 的情况具有逆元,则需要保证 \(\dfrac td \perp \dfrac md\),但是此时显然不一定成立,于是我们设 \(d' = \gcd(\dfrac td, \dfrac md)\),现在考虑同余式左侧应该如何除以 \(d'\)。思考 \(d'\) 此时是 \(\dfrac td\) 和 \(\dfrac md\) 的公共因子,因为 \(d\) 已经是 \(a,t,m\) 的最大公因数了,则此时 \(\dfrac ad,\dfrac td,\dfrac md\) 之间已经不存在公共因子了,若 \(d' \not \perp a\),则说明 \(\dfrac ad,\dfrac td,\dfrac md\) 之间还存在公共因子 \(d'\),与条件相悖,故 \(d' \perp a\)。
因为同余式左侧一定是 \(d'\) 的倍数(否则无法构成同余关系),故得证 \(d' \mid f_{p-1}\),换言之,就是 \(d'\) 一定会整除在 \(f_{p-1}\) 身上。于是式子变为:
好,现在我们再考虑能否将 \(\dfrac {f_{p-1}}{d'}\) 移项过去,要移项则必须保证 \(\dfrac{f_{p-1}}{d'} \perp \dfrac{m}{dd'}\),因为斐波那契数列具有性质 \(f_{p-1} \perp f_{p}\),并且我们又要必须保证右侧应当为 \(\gcd(\dfrac{f_{p-1}}{d'},\dfrac{m}{dd'})\) 的倍数,自然此时 \(\gcd(\dfrac{f_{p-1}}{d'},\dfrac{m}{dd'}) = 1\),故存在逆元,可以进行移项。
于是我们可以预处理三元组 \(\left( d,d',\dfrac{f_p}{\dfrac{f_{p-1}}{d'}} \bmod {\dfrac{m}{dd'}} \right)\),并用一个 map 来维护它。但是复杂度就直接飙升到令人无法接受的 \(O((\sum_{d \mid m}\sum_{d' \mid d} 1 )\log m)\)。于是我们考虑探究其中的性质。
我们回过头来看原先所写的 \(d' \mid f_{p-1}\),发现如果分两步枚举 \(d'\) 是非常笨的,因为我们还有 \(d' \mid \dfrac md\),我们又有 \(\gcd(\dfrac{f_{p-1}}{d'},\dfrac{m}{dd'}) = 1\),于是显然我们此时的 \(d'\) 只能等于 \(\gcd(f_{p-1},\dfrac md)\),故只需要枚举 \(d\) 和 \(p\) 就可以得到全部的三元组,此时的时间复杂度是 \(O(\sigma (m) \log m)\) 的,因为渐近意义下 \(\sigma(m)\) 是等价于 \(m \log \log m\) 的(具体证明参见 chenxia25 - P7325,所以枚举部分的复杂度是 \(O(m\log\log m \log m)\) 的,总时间复杂度为 \(O((m\log\log m+n)\log m)\)
注意:
- 若 \(f_{p-1}\) 和 \(f_p\) 等于 \(0\) 的时候我们的同余式就爆炸了,于是我们需要把这个特判掉。
代码实现
下面给出了关键部分的代码实现。
n=read();
m=read();
f[1] = 1;
for(int d = 1;d<m;++d){
	if(m%d==0){
		for(int p=2;p;++p){
			f[p] = (f[p-2] + f[p-1]) % (m/d);
			if(f[p-1] == 0 && f[p] == 1)
				break;
			if(!f[p-1] || !f[p])
				continue;
			int dd = __gcd(f[p-1],m/d);
			int val = 1 * f[p] * inv(f[p-1]/dd,m/dd/d) % (m/dd/d);
			if(!hashtable[make_tuple(d,dd,val)])
				hashtable[make_tuple(d,dd,val)] = p;
		}
	}
}
for(int i=1;i<=n;i++){
	a=read();
	b=read();
	if(a==0){
		printf("0\n");
		continue;
	}
	if(b==0){
		printf("1\n");
		continue;
	}
	b=(m-b)%m;
	int d = __gcd(a,__gcd(b,m));
	int dd = __gcd(b/d, m/d);
	int val = 1ll * a / d * inv(b/d/dd,m/d/dd) % (m/d/dd);
	int data = hashtable[make_tuple(d,dd,val)];
	if(!data)
		printf("-1\n");
	else
		printf("%lld\n",data);
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号