斐波那契(gcd+矩阵快速幂)

时间限制: 1 Sec  内存限制: 128 MB

题目描述

定义斐波那契数列F[0]=0, F[1]=1, F[n]=F[n-1]+F[n-2](n>=2)
小猪很喜欢这个数列,她听说你可以求F[n] mod 998244353,觉着很厉害,于是她会进行多次询问,每次询问给出五个整数n,a,b,c,d,她希望你求出gcd(a*F[n]+b*F[n+1],c*F[n]+d*F[n+1]) mod 998244353.

输入

第一个一个正整数q,表示询问数

输出

q行,每行一个[0,998244352]的非负整数,表示询问的答案。

样例输入 Copy

3
1 2 3 5 5
10 5 3 4 2
8 10 6 4 3

样例输出 Copy

5
2
6

提示

数据范围:
对20%的数据,n<=10
对另外10%的数据,b=d=0
对于另外20%的数据,b=c=0
对于100%的数据,1<=q<=100000,1<=n<=1018, 0<=a,b,c,d<=1000, a+b>=1,c+d>=1

 

预备知识:1. gcd(x, y) = gcd(y, x - ky) (k >= 0)

                  2. gcd(x, y) = gcd(y, x % y)

                  3. gcd(f[n], f[n + 1]) = 1

原式:gcd(a * f[n] + b * f[n + 1], c * f[n] + d * f[n + 1])

把d * f[n + 1]去掉

设b = k * d + x

原式 = gcd(a * f[n] + (k * d + x) * f[n + 1], c * f[n] + d * f[n + 1])

        = gcd(c * f[n] + d * f[n + 1], (a - c * k) * f[n] + x * f[n + 1]) (根据1知识点)

此时,a' = c, b' = d, c' = a - c * k, d' = x

通过上述继续消除d'f[n + 1],直到d' == 0为止

此时原式 = gcd(a' * f[n] + b' * f[n + 1], c' * f[n])

现在我们考虑几种情况:

     1. 如果c' == 0, 原式 = gcd(a' * f[n] + b' * f[n + 1],  0) = a' * f[n] + b' * f[n + 1], 直接矩阵快速幂求出f[n]和f[n + 1]即可

     2. 如果b' == 0, 原式 = gcd(a' * f[n], c' * f[n]) = f[n] * gcd(a', c'),f[n]通过矩阵快速幂求出即可

     3. c' != 0 && b' != 0 时:

            我们来推一个式子

               首先 gcd(a' * f[n] + b' * f[n + 1], f[n])

                       = gcd(b' * f[n + 1], f[n])(根据知识点1)

                       = gcd(b', f[n]) (根据知识点3)

                       = gcd(b', f[n] % b') (根据知识点2) 

            我们令 t = gcd(a' * f[n] + b' * f[n + 1], f[n]) 

            那么 gcd(a' * f[n] + b' * f[n + 1], c' * f[n])

                    =   t * gcd (\frac{a' * f[n] + b' * f[n + 1]}{t}, \frac{c'f[n]}{t})

                    =  gcd (a' * f[n] + b' * f[n + 1], t * c')

                    =  gcd ((a' * f[n] + b' * f[n + 1]) % (t * c'), t * c')

            最后直接矩阵快速幂求出f[n]和f[n + 1]即可

注意取模不同,和负数的情况,以及卡常

推荐使用gcd如下

LL gcd(LL a, LL b){
    if(a < 0) a =- a;
    if(b < 0) b =- b;
    if(a && b)
        while(a %= b ^= a ^= b ^= a);
    return a + b;
}

完整代码:

/**/
#pragma GCC optimize(3,"Ofast","inline")
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
typedef long long LL;
using namespace std;
 
const long long mod = 998244353;
 
int t, a, b, c, d;
LL n;
 
int Fib(LL n, LL mod){
	if(n <= 1) return n % mod;
	n--;
	int a[2][2] = {1, 1, 1, 0};
	int res[2][2] = {1, 0, 0, 1};
	LL c[2][2];
	while(n){
		if(n & 1){
			for (int i = 0; i < 2; i++){
				for (int j = 0; j < 2; j++){
					c[i][j] = (LL)res[i][0] * a[0][j] + (LL)res[i][1] * a[1][j];
				}
			}
			for (int i = 0; i < 2; i++){
				for (int j = 0; j < 2; j++){
					res[i][j] = c[i][j] % mod;
				}
			}
		}
		for (int i = 0; i < 2; i++){
			for (int j = 0; j < 2; j++){
				c[i][j] = (LL)a[i][0] * a[0][j] + (LL)a[i][1] * a[1][j];
			}
		}
		for (int i = 0; i < 2; i++){
			for (int j = 0; j < 2; j++){
				a[i][j] = c[i][j] % mod;
			}
		}
		n >>= 1;
	}
	return res[0][0];
}

LL gcd(LL a, LL b){
    if(a < 0) a =- a;
    if(b < 0) b =- b;
    if(a && b)
        while(a %= b ^= a ^= b ^= a);
    return a + b;
}

 
int main()
{
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
 
    scanf("%d", &t);
    while(t--){
        scanf("%lld %d %d %d %d", &n, &a, &b, &c, &d);
        while(d){
            int tmp = b / d;
            b -= d * tmp;
            a -= c * tmp;
            swap(b, d);
            swap(a, c);
             
        }
        // printf("%d %d %d %d\n", a, b, c, d);
        int res = c < 0 ? -c : c;
        if(!c){
            printf("%lld\n", (1LL * Fib(n, mod) * a % mod + 1LL * Fib(n + 1, mod) * b % mod + mod) % mod);
            continue;
        }
        if(!b){
            printf("%lld\n", 1LL * Fib(n, mod) * gcd(a, c) % mod);
            continue;
        }
        res *= gcd(Fib(n, b), b);
        printf("%lld\n", gcd(1LL * res, 1LL * Fib(n, res) * a + 1LL * Fib(n + 1, res) * b) % mod);
    }
 
    return 0;
}
/**/

 

你可能感兴趣的:(gcd,矩阵快速幂)