P7325 [WC2021] 斐波那契

该博客探讨了一道关于斐波那契数列的数学问题,如何将原问题转化为同余方程并解决。通过引入新的变量和去除最大公约数,作者展示了如何找到满足条件的解,并利用模运算性质优化计算。预处理部分提到循环节长度和约数特性,以及在处理过程中对模数不同的考虑。需要注意的是,求逆元时不能直接使用费马小定理,并且要处理模后结果为0的情况。
摘要由CSDN通过智能技术生成

一个做法很多的题!/se

我先讲我会的做法:

首先原来的问题可以变成 a F n − 1 + b F n = 0 m o d    m aF_{n-1}+bF_{n}=0\mod m aFn1+bFn=0modm,问这个式子的答案

于是可以考虑这个式子怎么冲,我们先变成下式

− a F n − 1 = b F n m o d    m -aF_{n-1}=bF_n\mod m aFn1=bFnmodm

如果有

− a b = F n F n − 1 m o d    m -\frac{a}{b}=\frac{F_n}{F_{n-1}}\mod m ba=Fn1Fnmodm

就好了qaq

感觉不够,我们同时除掉三者gcd,有

a ′ F n − 1 = b ′ F n m o d    m ′ a'F_{n-1}=b'F_n\mod m' aFn1=bFnmodm

这个式子相当优秀,我们考虑除掉三者gcd能保证至少两两gcd不会大于1,同时对于同余的两侧,一定有 g c d ( a ′ F n − 1 , m ) = g c d ( b ′ F n , m ) gcd(a'F_{n-1},m)=gcd(b'F_n,m) gcd(aFn1,m)=gcd(bFn,m)

有这个性质我们就可以冲一下了!

我们发现 g c d ( a ′ , m ′ ) = p , g c d ( b ′ , m ′ ) = q gcd(a',m')=p,gcd(b',m')=q gcd(a,m)=p,gcd(b,m)=q

则一定有 g c d ( F n , m ′ ) = p , g c d ( F n − 1 , m ′ ) = q gcd(F_n,m')=p,gcd(F_{n-1},m')=q gcd(Fn,m)=p,gcd(Fn1,m)=q

那么我们可以考虑把每一对n,n-1拿出来,然后求出这个p,q,然后再计算出 ( p , q , F n / F n − 1 m o d    m / p q ) (p,q,F_{n}/F_{n-1}\mod m/pq) (p,q,Fn/Fn1modm/pq)这个三元组插入map

然后询问的时候我们同除三者的gcd,然后算出p,q,然后算出左边那个 − a b -\frac{a}{b} ba,再去查询一个最小的回答即可

预处理多少?循环节长度是6n的,因此不需要太多就好

注意我们mod数不同结果不同,因此还需要对于所有mod的数计算这个三元组,但是很显然我们这个mod数是m的约数

为什么m可以除p*q?

因为p,q互质

注意求逆元不能用费马小定理…

然后w还可能后面那个东西是0,因此要再去mod一次

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 2e5 + 7;

int n, m;
int gcd(int x, int y) {
	return (y == 0) ? x : gcd(y, x % y);
}
void exgcd(int a, int b, int &x, int &y) {
	if( a == 1 ) {
		x = 1, y = 0;
	} else exgcd(b, a % b, y, x), y -= a / b * x;
}
int inv(int a, int m) {
	int x, y;
	exgcd(a, m, x, y);
	return (x % m + m) % m;
}


struct rec {
	int x, y, z;
	rec(int X = 0, int Y = 0, int Z = 0): x(X), y(Y), z(Z) {};
	bool operator<(const rec &a)const {
		return (x == a.x ? (y == a.y ? (z < a.z) : (y < a.y)) : x < a.x);
	}
};
map<rec, int> mp[MAXN];

int f[3], cnt;
inline void upd(int x) {
	int p = f[2];
	f[2] = (f[2] + f[1]) % x;
	f[1] = p;
	++cnt;
}
inline void init() {
	for(int P = 2; P <= m; ++P) {
		if(m % P == 0) {
			f[1] = 0;
			f[2] = 1;
			cnt = 1;
			while(1) {
				int p = gcd(f[2], P);
				int q = gcd(f[1], P);
				int mod = P / p / q;
				int w = 1ll * (f[2] / p) * inv((f[1] / q), mod) % mod;
				if(mp[P].find(rec(p, q, w)) == mp[P].end())mp[P][rec(p, q, w)] = cnt;
				upd(P);
				if(f[2] == 1 && f[1] == 0)break;
			}
		}
	}
}

int main() {
	// freopen("test.in", "r", stdin);
	// freopen("test.out", "w", stdout);
	scanf("%d%d", &n, &m);
	init();
	for(int i = 1, a, b; i <= n; ++i) {
		scanf("%d%d", &a, &b);
		if(a == 0) {
			puts("0");
			continue;
		} else if(b == 0) {
			puts("1");
			continue;
		}
		int P = m;
		int t = gcd(a, gcd(b, P));
		a /= t;
		b /= t;
		P /= t;
		int p = gcd(a, P);
		int q = gcd(b, P);
		a /= p;
		b /= q;
		int mod = P / p / q;
		int w = (mod - 1ll * a * inv(b, mod) % mod) % mod;
		if(mp[P].find(rec(p, q, w)) == mp[P].end())puts("-1");
		else printf("%d\n", mp[P][rec(p, q, w)]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值