HDU 5667 Sequence (矩阵快速幂 + 费马小定理)

题目链接


思路:我们手写几步fn的话,就能看出来,事实上到最终是一堆a^b相乘,所以我们要算的其实就是a的几次方。

1.首先我们要算,a到底要乘多少次

我们假设fn(ab)p[n]

可得: (ab)p[n]= a* ((ab)p[n-1])c * ((ab)p[n-2]); n > 2

从上式可得:p[n]=c*p[n-1]+p[n-2]+1;

从上述的递推式,我们就可以来构造矩阵了

p[n]     c    1    1    p[n-1]
p[n-1]    =  1   0    0   *   p[n-2]
1      0   0     1    1
然后我们可以继续用这样的关系表示乘号右边的矩阵,因此递推到最后就变成了

所以我们只要求得初始系数矩阵的(n-2)次,再做运算,就可以得到pn,最后再乘b就行。


2.其次如果求得的pn特别大怎么办?这时我们需要对pn取模,那怎样取模才不会影响最后的运算呢?这里就需要“费马小定理”。

费马小定理: 假如p是质数,且gcd(a,p)=1,那么a^(p-1)%p=1

一开始我看了这个定理仍然不知道怎么取模,然后学长跟我提了几句:①a^p = a * a^p-1 ② (a^(p-1)) % p = 1 那么 a^p % p = a % p

  直白点的意思就是,对如果a,p互质,那么对a来说,a的几次方再对模p的结果,事实上是一个循环,这个循环的大小就是p-1。
因此我们需要在第一步里,求pn的过程中对p-1取模。

3.此时通过第一二两步已经得到了a最终需要的指数,这就是一个普通的快速幂。


总结:初学矩阵快速幂,对这类型的之前还是处于一知半解状态,我认为关键是熟练从递推式来构造矩阵的过程。

代码:

import java.util.*;
class Matrix{
	long [][] m = new long[3][3];
}
public class Main{
	static Scanner sc = new Scanner(System.in);
	static int t;
	static long n,a,b,c,p;
	static final int MOD = 10000;
	static long p(long x, long k) {
		long ans = 1;
		while(k > 0) {
			if((k&1) == 1)
				ans = (ans * x) % p;
			x = (x * x) % p;
			k >>= 1;
		}
		return ans;
	}
	static Matrix multi(Matrix a, Matrix b) {
		Matrix tmp = new Matrix();
		for(int i = 0; i < 3; i++) {
			for(int j = 0; j < 3; j++) {
				for(int k = 0; k < 3; k++) {
					tmp.m[i][j] += (a.m[i][k] * b.m[k][j]) % (p-1);
				}
			}
		}
		return tmp;
	}
	static long matrix_p(Matrix a, long k) {
		Matrix ans = new Matrix();
		ans.m[0][0] = 1; ans.m[1][1] = 1; ans.m[2][2] = 1;
		while(k > 0) {
			if((k&1) == 1)
				ans = multi(ans,a);
			a = multi(a,a);
			k >>= 1;
		}
		return (ans.m[0][0]*b + ans.m[0][2]*b) % (p-1);
	}
	public static void main(String[] args) {
		t = sc.nextInt();
		while(t-- > 0) {
			n = sc.nextLong();
			a = sc.nextLong(); b = sc.nextLong(); c = sc.nextLong();
			p = sc.nextLong();
			if(n == 1 || a%p == 1) {//1 or mod == 1
				System.out.println(1);
			}
			else if(n == 2) {//2
				if(a%p == 0)//这题还有个坑点,就是如果a%p == 0的话,结果肯定是0
					System.out.println(0);
				else {
					long ans = p(a,b);
					System.out.println(ans);
				}
			}
			else { //otherwise
				if(a%p == 0)
					System.out.println(0);
				else {
					Matrix x = new Matrix();
					x.m[0][0] = c;
					x.m[0][1] = 1; x.m[0][2] = 1; x.m[1][0] = 1; x.m[2][2] = 1;
					long k = matrix_p(x,n-2);
					System.out.println(p(a,k));
				}
			}
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值