题解 P7325

前言

数学符号约定

  1. \(a,b,p\):表示任意自然数。
  2. \(F_x\):表示广义斐波那契数列的第 \(x\) 项。
  3. \(f_x\):表示普通斐波那契数列的第 \(x\) 项.

如非特殊说明,将会按照上述约定书写符号。

题目分析

首先引入一条定理:

普通斐波那契数列在模 \(m\) 意义下纯循环,且循环节为 \(O(m)\)

证明可以去看 wlzhouzhuan 的博客,碍于篇幅限制不多赘述。

然后我们定义 \(F_0 = a,\, F_1 = b\),则显然我们有:\(F_p = af_{p-1} + bf_{p}\),证明平凡,如果想不出来可以手玩一下试试。

此时,我们的目标是对于 \(a,b\) 求出 \(p\) 使其满足下式:

\[af_{p-1} + bf_p \equiv 0 \pmod {m} \]

移项,得:

\[af_{p-1} \equiv -bf_p \pmod m \]

此时,我们不妨令 \(t = -b\),则我们有:

\[af_{p-1} \equiv tf_p \pmod m \]

观察上式,发现如果我们能将 \(f_{p-1}\)\(t\) 分别移项,然后预处理 \(\dfrac {f_p}{f_{p-1}}\) 问题就十分的简单了,但是因为 \(m\) 不为质数(即两者不一定互质),故我们不能这么做。

然而,俗话说的好,没有条件就让我们创造条件,先考虑对原式化简,设 \(d = \gcd(a,t,m)\),则原式变为:

\[\frac{a}{d} f_{p-1} \equiv \frac{t}{d} f_p \pmod {\frac{m}{d}} \]

如果我们想让 \(\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}\) 身上。于是式子变为:

\[\begin{aligned} \frac{a}{d} \frac{f_{p-1}}{d'} \equiv \frac{t}{dd'} f_p \pmod {\frac{m}{dd'}}\\ \frac{a}{d} \left( \frac{t}{dd'}\right) ^ {-1} \frac {f_{p-1}}{d'} \equiv f_p \pmod {\frac{m}{dd'}} \end{aligned} \]

好,现在我们再考虑能否将 \(\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);
}
posted @ 2023-04-27 21:41  Larry76  阅读(45)  评论(0)    收藏  举报