扩展欧拉定理
扩展欧拉定理
前置芝士
欧拉定理
定义
对于a与m不一定互质的情况,有:
推论
当 \(m=1\) 时明显成立,接下来讨论 \(m\not=1\) 的情况。
对于 \(\gcd(a,m)=1\) 的情况,因为 \(a^{\varphi(m)}\equiv 1\pmod m\) ,所以每 \(\varphi(m)\) 个 \(a\) 就相当于乘 \(1\) 。于是只需要算
\(c\mod\varphi(m)\) 次。
对于 \(c<\varphi(m)\) 的情况,直接爆算
下面证明第三种情况。
先证明 \(a\) 是一个质数的情况:
引理1
\(\forall p\) 为质数,\(r\in Z^+\) ,都有 \(\varphi(p^r)=(p-1)\times p^{r-1}\) 。
证明:
由于p是一个质数,所以 \(1\sim (p^r-1)\) 中有且仅有 \(i\times p,i\in (0,p^{r-1})\) 与 \(p^r\) 不互质。
于是 \(\varphi(p^r)=p^r-p^{r-1}=p^{r-1}\times (p-1)\)
证毕
引理2
\(\forall~k\in Z\),\(\exists~a,b,x,y\in Z^+,x^a\times y^b=k\) ,都有 \(a,b\leq\varphi(k)\) 。
证明
先考虑k为一个质数p的r次幂的情况。根据引理1有:
下面说明 \((p-1)\times p^{r-1}\geq r\) 。
当p=2时:
经验证\(~r=1,2,3~\)时成立。当\(~r>3~\)时按照r的大小做数学归纳,可证正确性。
当\(~p~>~2~\)时,不等号左侧增大,右侧不变,不等式仍然成立。
考虑k是多个质数幂时的情况,按照质数个数做数学归纳,正确性成立。
任意组合质数,引理得证。
证毕
引理三:
\(\exists~r~\leq~c~,a^{\varphi(m)+r}~\equiv~a^r\pmod m。\)
证明:
令 \(m=t\times a^r\) ,其中 \(\gcd(t,a)=1\) ,t的存在性显然
因为 \(\gcd(t,a)=1\) ,且 \(\varphi\) 函数是一个积性函数,所以 \(\varphi(t)|\varphi(m)\) 。
根据欧拉定理,\(a^{\varphi(t)}~\equiv~1~\pmod t\) ,于是:
同余式同乘 \(a^r\) ,于是:
已经证明可以构造出这样的r。根据引理2,\(r~\leq~\varphi(m)\)。又 \(c~\geq~\varphi(m)\),于是可证构造出的\(r~\leq~c\)。定理得证。
证毕
于是:
对上式做数学归纳,可得\(a^c~\equiv~a^{c+k\varphi(m)}~,~k~\in~Z\),需保证指数为正。
于是最小的合法的指数即为\(c\mod\varphi(m)~+~\varphi(m)\)。
于是有:
当a是一个质数的幂次时,设 \(a=p^k\) ,则
当a时多个质数次幂的乘积时,依据唯一分解定理做数学归纳,即证正确性。证毕。
代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a,m,b;
inline ll read(ll m){
register ll x=0,f=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)){
x=x*10+ch-'0';
if(x>=m) f=1;
x%=m;ch=getchar();
}
return x+(f==1?m:0);
}
ll phi(ll n){
ll ans=n,m=sqrt(n);
for(ll i=2;i<=m;i++){
if(n%i==0){
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) ans=ans/n*(n-1);
return ans;
}
ll fast_pow(ll a,ll b,ll p){
ll ret=1;
for(;b;b>>=1,a=a*a%p)
if(b&1) ret=ret*a%p;
return ret;
}
int main()
{
scanf("%lld%lld",&a,&m);
b=read(phi(m));
printf("%lld\n",fast_pow(a,b,m));
return 0;
}
例题
CF17D Notepad
思路
这道题的范围是 \(2\leq a\leq 10^{1000,000}\) ,\(1\leq n\leq 10^{1000,000}\) ,\(1\leq p\leq 10^9\)
一个\(a\)进制的数字的每一位有\(a\)种取值,但是题目要求不能有前导0,所以第一位只有\(a-1\)种取值。
所以满足要求的\(a\)进制数有 \((a-1)a^{n-1}\) 个。
所以题目要求我们求得就是
(但是当 \(p|(a-1)a^{n-1}\) 时应输出 \(p\) 。因为此时最后一页有0个字,相当于这一页还没写,也就不是最后一页了)
\(a,n\) 很大,但是 \(p\leq 10^9\) 。根据扩展欧拉定理,有 \(a^{n-1}\equiv a^{(n-1)\mod\varphi(p)+\varphi(p)} \pmod p\) 。所以我们可以边读入边取模。反正 \(ab\mod p\equiv(a\mod p)\times (b\mod p)~\mod p\) 。
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int N=1000010;
char sa[N],sn[N];
ll p,a,n,phi,q,ans;
int len1,len2;
bool flag;
ll power(ll x,ll k)
{
ll ans=1;
for (;k;k>>=1,x=x*x%p)
if (k&1) ans=ans*x%p;
return ans;
}
int main()
{
scanf("%s %s %lld",sa+1,sn+1,&p);
phi=q=p; len1=strlen(sa+1); len2=strlen(sn+1);
for (ll i=2;i*i<=q;i++)
if (!(q%i))
{
phi=phi/i*(i-1);
while (!(q%i)) q/=i;
}
if (q>1) phi=phi/q*(q-1);
for (int i=1;i<=len1;i++)
a=(a*10+sa[i]-48)%p;
for (int i=len2;i>=1;i--) //n-1
if (sn[i]==48) sn[i]='9';
else
{
sn[i]--;
break;
}
for (int i=1;i<=len2;i++)
{
n=n*10+sn[i]-48;
if (n>=phi) flag=1;
n%=phi;
}
if (flag) n+=phi;
ans=((a-1)*power(a,n)%p+p)%p;
if (ans) printf("%lld",ans);
else printf("%lld",p);
return 0;
}