十进制快速幂&&十进制矩阵快速幂

想必大家都会二进制快速幂, 普通的快速幂中, 将指数拆为二进制数,基数为 2 的次方。

十进制快速幂仅仅是将基数变为了10的次方,只要对普通快速幂的思想了解, 这个就很简单了

 例如求7^{365} = 7^{100^{3}} * 7^{10^{6}}*7^{5} 

ll t_pow(ll a, ll b)
{
	ll res = 1;
	while (b > 0)
	{
		ll now = a;
		for (int i = 1; i <= b % 10; i++)  // 当前位数字为几, 即乘几次
			res = (res * a) % mod;
		for (int i = 2; i <= 10; i++) // a = a ^ 10
			a = a * now % mod;
		b /= 10;
	}
	return res;
}

显然每次我们都要有个乘9次的操作, 可以用快速幂的思想倍增求, 这样就可以优化到3次

ll t_pow(ll a, ll b)
{
	ll res = 1;
	while (b > 0)
	{
		for (int i = 1; i <= b % 10; i++)
			res = (res * a) % mod;
		ll x = a * a % mod;    // a ^ 2
		ll y = x * x % mod;   // a ^ 4
		ll z = y * y % mod;  // a ^ 8  
		a = z * x % mod;  // a ^ 10
		b /= 10;
	}
	return res % mod;
}

至于矩阵快速幂的话, 跟普通的矩阵快速幂差别也就仅仅在这里

例如牛客多校第五场 B: generator 1

https://ac.nowcoder.com/acm/contest/885/B

这题给出x_{1} ,x_{0},A, B  满足 x_{i} = a * x_{i - 1} + b * x_{i - 2} (i \geq 2)

求第n项 n\leq 10^{10^{6}}

显然直接用二进制快速幂求会超时。

\begin{bmatrix} A&B \\ 1 &0 \end{bmatrix} * \begin{bmatrix} x_{1}\\ x_{0} \end{bmatrix} = \begin{bmatrix} A *x_{1} + B * x_{0}\\ x1 \end{bmatrix} = \begin{bmatrix} x_{2}\\ x_{1} \end{bmatrix}

\begin{bmatrix} A&B \\ 1 &0 \end{bmatrix} * \begin{bmatrix} F_{n}\\F_{n - 1} \end{bmatrix} = \begin{bmatrix} F_{n + 1}\\F_{n} \end{bmatrix}

x_{1}, x_{0}做一次矩阵乘法, 得到的即是F_{2}F_{1}

所以乘n次后, 取m[1][0]即是答案


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
ll A, B, x0, x1;
ll m;
char s[N];
struct M{
	ll m[2][2];
	M() 
	{
		memset(m, 0, sizeof m);
	}
};
M mul(M a, M b)
{
	M ans;
	for (ll i = 0; i < 2; i++)
		for (ll j = 0; j < 2; j++)
		{
			for (ll k = 0; k < 2; k++)
				ans.m[i][j] = (ans.m[i][j] + a.m[i][k] * b.m[k][j] % m) % m;
		}
	return ans;
}
void fpow(ll n)
{
	M a, b;
	a.m[0][0] = A;
	a.m[0][1] = B;
	a.m[1][0] = 1;
	b.m[0][0] = b.m[1][1] = 1;
	while (n >= 0)
	{
		ll cnt = s[n] - '0';
		M now = a;
		for (int i = 1; i <= cnt; i++)
			b = mul(b, a);	
		M c[2];
		a = mul(a, a); // 2
		c[0] = mul(a, a); // 4
		c[1] = mul(c[0], c[0]); // 8
		a = mul(a, c[1]);
		n--;
	}
	M ans;
	ans.m[0][0] = x1;
	ans.m[1][0] = x0;
	ans = mul(b, ans);
	cout << ans.m[1][0] << endl;
}
int main(){
	cin >> x0 >> x1 >> A >> B;
	scanf("%s", s);
	cin >> m;
	ll len = strlen(s);
	fpow(len - 1);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值