【洛谷】【擴展歐幾里得算法】P4031 [Code+#2]可做題2

洛谷 P4031 [Code+#2]可做題2

題目大意

◇題目傳送門◆

給定一個廣義斐波那契數列的第一項a1=ia_1=i,和第二項的範圍a2[l,r]a_2\in [l,r],求滿足akm(mod  p)a_k\equiv m(\mod p)的數列的個數。

分析

先找一下aa的規律,設a2=xa_2=x,則有:a1=i,a2=x,a3=i+x,a4=i+2x,a5=2i+3xa_1=i,a_2=x,a_3=i+x,a_4=i+2x,a_5=2i+3x\ldots

多算幾個可以發現:ak=fk3i+fk2xa_k=f_{k-3}\cdot i+f_{k-2}\cdot x

那麼問題轉化爲求關於xx的同餘方程
fk3i+fk2xm(mod  p) f_{k-3}\cdot i+f_{k-2}\cdot x\equiv m(\mod p)

在區間[l,r][l,r]上的解的個數。

考慮移項:

fk2xmfk3i(mod  p) f_{k-2}\cdot x\equiv m-f_{k-3}\cdot i(\mod p)

將它轉化爲不定方程的形式:

fk2x+py=mfk3i f_{k-2}\cdot x+p\cdot y=m-f_{k-3}\cdot i

於是直接套用擴展歐幾里得求出它的一個特解,再利用通解公式求出區間上解的個數即可。

注意kk比較大,求解fk2f_{k-2}fk3f_{k-3}時要用矩陣乘法加速

參考代碼

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;

ll p;

struct Matrix {
	ll mat[2][2];
	Matrix() {mat[0][0] = mat[1][1] = 1, mat[1][0] = mat[0][1] = 0;}
	Matrix(int typ) {
		if(typ == 0) mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = 0;
		if(typ == 1) mat[0][0] = 0, mat[1][0] = mat[0][1] = mat[1][1] = 1;
	}
	inline ll * operator [] (int x) {return mat[x];}
	inline const ll * operator [] (const int &x) const {return mat[x];}
	friend Matrix operator * (const Matrix &lhs, const Matrix &rhs) {
		Matrix ret(0);
		for(int k = 0; k < 2; k++)
			for(int i = 0; i < 2; i++)
				for(int j = 0; j < 2; j++)
					ret[i][j] = (ret[i][j] + lhs[i][k] * rhs[k][j] % p) % p;
		return ret;
	}
};


Matrix QuickPow(Matrix a, ll k) {
	Matrix ret;
	while(k) {
		if(k & 1) ret = ret * a;
		a = a * a;
		k >>= 1;
	}
	return ret;
}

ll i, l, r, m, k;

ll ExGCD(ll a, ll b, ll &x, ll &y) {
	if(b == 0) {
		x = 1, y = 0;
		return a;
	}
	ll g = ExGCD(b, a % b, y, x);
	y -= a / b * x;
	return g;
}

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	int _;
	scanf("%d", &_);
	while(_--) {
		scanf("%lld %lld %lld %lld %lld %lld", &i, &l, &r, &k, &p, &m);
		Matrix tmp(1);
		tmp = QuickPow(tmp, k - 1);
		i %= p;
		m = (m - (tmp[0][0] % p * i) % p + p) % p;
		ll x, y;
		ll gcd = ExGCD(tmp[0][1], p, x, y);
		if(m % gcd != 0) {
			puts("0");
			continue;
		}
		x *= (m / gcd), p /= gcd;
		if(x >= 0) x = (x % p - p);
		printf("%lld\n", (r - x) / p - (l - 1 - x) / p);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章