hdu 3221 Brute-force Algorithm 指数循环节

观察题目的程序可以很容易得到F[1] = a ,F[2] = b , F[3] = F[1]*F[2] , ....   F[n] = F[n-1]*F[n-2]


由于n <= 10^9,应该是要用到二分幂了,观察可得,F[1] = a, F[2] = b, F[3] = a*b , F[4] = a * b^2 , F[5] = a^2 * b^3,

也就是说a的次幂和b的次幂都是斐波那契数列,这样就很简单了,然后根据

A^x % m = A^(x%phi(m)+phi(m)) % m (x >= phi(m))
不清楚这个公式的可以戳这里看看 http://blog.csdn.net/u010710717/article/details/9664825


a和b的次幂刚好是斐波那契数列中相邻的两个数,所以可以一次性得到,不过要判断下要不要加上Phi(m),具体见代码。


#include <stdio.h>
#define LL __int64

const int maxn = 100000;
bool vis[maxn];
int pri[maxn], num;
LL rec[2], q[2][2], qq[2][2], ss[2];

void get_prime() {     // 素数筛选法
	int i, j;
	vis[1] = 1;
	for(i = 2;i*i <= maxn; i++) if(!vis[i]) 
		for(j = i*i;j <= maxn; j += i)	vis[j] = 1;
	num = 0;
	for(i = 2;i <= maxn; i++) if(!vis[i])
		pri[num++] = i;
}

LL euler(LL n) {
	LL ans = n;
	for(int i = 0;i < num && pri[i]*pri[i] <= n; i++) {
		if(n%pri[i] == 0) {
			ans = ans-ans/pri[i];
			while(n%pri[i] == 0)	n /= pri[i];
		}
	}
	if(n > 1)	ans = ans - ans/n;
	return ans;
}

void recpow(LL mod, LL n) {
	int i, j, k;
	LL now1 = rec[0], now2 = rec[1];
	int ok = 0;
	for(i = 0;i < n; i++) {
		LL now = now1 + now2;
		if(now >= mod) {
			if(i < n-1) ok = 2;   // a和b的次幂都 >= mod
			else	ok = 1;  //  唯独b的次幂 >= mod
			break;
		}
		now1 = now2; now2 = now;
	}
	q[0][0] = 0;
	q[0][1] = q[1][0] = q[1][1] = 1;
	while(n) {
		if(n&1) {
			for(i = 0;i < 2; i++)
				ss[i] = rec[0]*q[0][i] + rec[1]*q[1][i];
			for(i = 0;i < 2; i++)
				rec[i] = ss[i]%mod;
		}
		for(i = 0;i < 2; i++)
			for(j = 0;j < 2; j++) {
				qq[i][j] = 0;
				for(k = 0;k < 2; k++)
					qq[i][j] = (qq[i][j] + q[i][k]*q[k][j])%mod;
		}
		for(i = 0;i < 2; i++)
			for(j = 0;j < 2; j++)
				q[i][j] = qq[i][j];
		n /= 2;
	}
	if(ok >= 1)	rec[1] += mod;
	if(ok >= 2)	rec[0] += mod;
}

LL powmod(LL x, LL n, LL mod) {
	LL ret = 1;
	while(n) {
		if(n&1) ret = ret*x%mod;
		x = x*x%mod;
		n /= 2;
	}
	return ret;
}

void solve(LL a, LL b, LL p, LL n) {
	LL cur = euler(p);   // 求模的欧拉函数
	rec[0] = 0;rec[1] = 1;
	recpow(cur, n);  //   求a和b 的次幂
	LL ans = powmod(a, rec[0], p);
	ans = ans*powmod(b, rec[1], p)%p;
	printf("%I64d\n", ans);
}

int main() {
	get_prime();
	LL a, b, p, n;
	int t, cas = 1;
	scanf("%d", &t);
	while(t--) {
		scanf("%I64d%I64d%I64d%I64d", &a, &b, &p, &n);
		printf("Case #%d: ", cas++);
		if(n == 1)	printf("%I64d\n", a%p);    //n <= 2 直接输出
		else if(n == 2)	printf("%I64d\n", b%p);
		else	solve(a, b, p, n-2);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值