题解 [WC2021] 斐波那契

传送门

非常不可想不可做

核心在于对 \(a\equiv b\pmod m\and g=\gcd(a, b, m)\),有 \(\frac{a}{g}\equiv\frac{b}{g}\pmod{\frac{m}{g}}\) 这一性质的灵活运用
发现限制条件等价于 $$af_{n-1}+bf_n\equiv 0\pmod m$$
换元 \(b=-b\) 并移项得

\[af_{n-1}\equiv bf_n\pmod m \]

\(m\) 是质数的时候可以将 \(a, b\)\(f_{n-1}, f_n\) 分离,即

\[\frac{a}{b}\equiv \frac{f_n}{f_{n-1}}\pmod m \]

  • 斐波那契数列在 \(\bmod m\) 意义下的循环节是 \(O(m)\) 级别的,据说不会超过 \(6m\)

那么就可以预处理每个 \(\frac{f_n}{f_{n-1}}\) 的取值了

然后考虑模数不是质数的情况:
先把能除掉的公因数除掉

\[g=\gcd(a, -b, m), a'=\frac{a}{g}, b'=\frac{-b}{g}, m'=\frac{m}{g} \]

条件变为

\[a'f_{n-1}\equiv b'f_n\pmod{m'} \]

但是 \(b'\) 还是没办法除过来,\(\gcd(b', m')\) 可以 \(>1\)
发现对 \(m'\) 取模后并不影响和 \(m'\)\(\gcd\)
那么

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

尝试把 \(\gcd(b', m')\)\(\gcd(f_{n-1},m')\) 孤立出来

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

发现

\[\gcd(\gcd(a',m'),\gcd(b',m'))=1 \]

并且

\[\gcd(f_n,f_{n-1})=1 \]

所以两边都是最简分数
所以

\[\gcd(f_{n-1},m')=\gcd(b',m') \]

\(g\) 等于这个东西,发现现在等式两边和模数都有这个东西,再除掉之后就互质了
于是就可以将 \(b'\) 除过来了
对每个可能的 \(mod\) 预处理 \((g, \frac{f_n}{\frac{f_{n-1}}{g}})\),扔到 map 里就好了
复杂度 \(O(m\log^2 m)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
struct pair_hash{inline size_t operator () (pair<int, int> a) const {return 13131llu*a.fir+998244353*a.sec;}};
unordered_map<pair<int, int>, int, pair_hash> mp[N], vis;
inline int gcd(int a, int b) {return !b?a:gcd(b, a%b);}
void exgcd(int a, int b, int& x, int& y) {
	if (!b) {x=1; y=0; return ;}
	exgcd(b, a%b, y, x);
	y-=a/b*x;
}
inline ll qinv(int a, int b) {int x, y; exgcd(a, b, x, y); return (x%b+b)%b;}

signed main()
{
	n=read(); m=read();
	for (int mod=1; mod<=m; ++mod) if (!(m%mod)) {
		vis.clear();
		// cout<<"mod: "<<mod<<endl;
		for (int i=2,a=1,b=1; vis.find({a, b})==vis.end(); ++i,swap(a,b),b=(b+a)%mod) {
			// cout<<"i: "<<i<<' '<<a<<' '<<b<<endl;
			vis[{a, b}]=1;
			if (!a||!b||gcd(gcd(a, mod), gcd(b, mod))!=1) continue;
			int g=gcd(a, mod), val=b*qinv(a/g, mod/g)%(mod/g);
			// if (gcd(a/g, mod)!=1) cerr<<"error: "<<a<<' '<<mod<<' '<<g<<" gcd("<<a/g<<','<<mod<<")="<<gcd(a/g, mod)<<endl;
			// assert(gcd(a/g, mod)==1);
			// cout<<"g: "<<g<<' '<<b*qinv(a/g, mod)%mod<<endl;
			if (mp[mod].find({g, val})==mp[mod].end()) mp[mod][{g, val}]=i;
		}
	}
	for (int i=1,a,b; i<=n; ++i) {
		a=read(); b=read();
		if (!a) {puts("0"); continue;}
		if (!b) {puts("1"); continue;}
		a=a%m; b=m-b%m;
		int t=gcd(a, gcd(b, m)), _a=a/t, _b=b/t, _m=m/t;
		// cout<<"abm: "<<_a<<' '<<_b<<' '<<_m<<endl;
		int g=gcd(_b, _m), val=_a*qinv(_b/g, (_m/g))%(_m/g);
		// cout<<"g: "<<g<<' '<<val<<endl;
		if (mp[_m].find({g, val})!=mp[_m].end()) printf("%d\n", mp[_m][{g, val}]);
		else puts("-1");
	}

	return 0;
}
posted @ 2022-07-07 16:36  Administrator-09  阅读(4)  评论(0编辑  收藏  举报