「学习笔记」光速幂

对于 \(a^b \bmod p\),快速幂单次询问是 \(O(\log b)\) 的。

而有的题目可能要去掉 \(\log\),就用到了光速幂。

\(b=k \times s+t\ \ (t \le s)\)

\(a^b \equiv a^{k \times s} \times a^t\ (\bmod\ p)\)

于是就可以预处理出 \(a^1,a^2,\dots,a^s,a^{2s},a^{3s},\dots,a^{\left\lceil{\frac{p}{s}}\right\rceil\times s}\)

当然要 \(\bmod\ p\)

然后 \(a^b \bmod p\) 就可以 \(O(1)\) 查询了。

预处理在 \(s=\sqrt{p}\) 时,时间复杂度最小,为 \(O(\sqrt{p})\)

根据欧拉定理 \(a^b \equiv a^{b \bmod \phi(p)}\ (\bmod p)\)

可以先把 \(b \bmod \phi(p)\)并且预处理到 \(\phi(p)\) 就可以了,到 \(p\) 只是方便写。

//a^ks*a^t % p (t<=s)
#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long

using namespace std;

const int BL=1<<16;	//BL=sqrt(p) 
ll B;
ll a,b,p;
ll qp[BL+10][2];	//0:a^t%p	1:a^is%p

ll read()
{
	ll x=0;
	char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x;
}

void init()
{
	B=sqrt(p);
	qp[0][0]=qp[0][1]=1;
	for(int i=1;i<=B;i++)
		qp[i][0]=qp[i-1][0]*a%p;
	for(int i=1;i<=B;i++)
		qp[i][1]=qp[i-1][1]*qp[B][0]%p;
}

ll phi(int x)
{
	int res=x;
	for(int i=2;i*i<=x;i++)
	{
		if(x%i==0) res=res/i*(i-1);
		while(x%i==0) x/=i;
	}
	if(x>1) res=res/x*(x-1);
	return res;
}

ll qpow(ll b)
{
	b%=phi(p);
	return qp[b%B][0]*qp[b/B][1]%p;
}

int main()
{
	a=read();b=read();p=read();
	init();
	printf("lld\n",qpow(b));
	return 0;
}
posted @ 2021-02-20 21:07  Acestar  阅读(688)  评论(1编辑  收藏  举报