数论欧拉降幂基础

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/w_udixixi/article/details/100739216

欧拉降幂公式

这里是大佬对欧拉降幂公式的证明,找了好久找到了一个看上去比较人性化的,但是还是看不懂,如果日后有兴趣 再回过头来看一下吧
https://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/86742254

欧拉定理

  • 若两个正整数a,m互质,则 a φ ( m ) 1 a^{\varphi(m)}\equiv 1 m o d mod m m

欧拉降幂公式

用公式简单的表示为:
a b { a b % φ ( m ) ( m o d   m )                   g c d ( a , b ) = 1 a b % φ ( m ) + φ ( m ) ( m o d   m )          g c d ( a , b ) ̸ = 1 , b > = φ ( m ) a b ( m o d   m )                           g c d ( a , b ) ̸ = 1 , b < φ ( m ) a^b\equiv\begin{cases} a^{b\% \varphi (m)} (mod \ m) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ gcd(a,b)=1\\ a^{b\% \varphi (m)+\varphi(m)} (mod \ m) \ \ \ \ \ \ \ \ gcd(a,b)\not=1,且b>=\varphi (m)\\ a^b(mod \ m) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ gcd(a,b)\not=1,且b<\varphi (m) \end{cases}

也就是说如果对于求解 a b % m o d a^b\%mod ,因为b太大了不能使用快速幂,这时候就可以通过欧拉降幂,实现对b的不断更新,能将b控制在 [ 0 , 2 φ ( m ) ) [0,2\varphi(m)) 的范围,从而再用快速幂解决这一类问题


例题1
FZU1759
属于模板题
b范围太大所以需要读入字符串类型,然后用欧拉降幂进行处理

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (int x=a;x<=b;x++)
#define repp(x,a,b) for (int x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=2e6+7;
const int INF=1e9;
const ll INFF=1e18;
char s[maxn];
ll a,mod;
ll qpow(ll a,ll b,ll mod)//快速幂板子
{
    ll res=1;
    while(b)
    {
        if (b&1)res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}

ll phi(ll n)//欧拉函数板子
{
    ll res=n;
    for (int i=2;i*i<=n;i++)
    {
        if (n%i==0)
        {
            res=res/i*(i-1);
            while(n%i==0)n/=i;
        }
    }
    if (n>1)res=res/n*(n-1);
    return res;
}

int main()
{
    while(~scanf("%lld%s%lld",&a,s,&mod))
    {
        ll l=strlen(s);
        ll p=phi(mod);
        ll b=0;
        repp(i,0,l)
        {
            ll newb=b*10+s[i]-'0';
            if (newb>=p)b=newb%p+p;
            else b=newb;
        }
        WW(qpow(a,b,mod));
    }
    return 0;
}

例题2
这题画重点
对于初学的我还是太难了,这个递推式竟然没有手动去推出来…
Gym - 101550E - Exponial

我的思路:题意很简单清晰,但是实现起来一直想不到优化的方法,只能想到O(n)的欧拉降幂,从上面往下降,直到降到 n x n^x ,显然超时
当n=5的时候数值就已经大到10的几万次,也就是说当n>5的时候是肯定可以用欧拉降幂的 b &gt; = φ ( m ) b&gt;=\varphi(m) 对应的公式的,然后就一直想不出来怎么简化时间了

大神思路:现在给定n,m
设所求式子为 f ( n ) f(n) ;
由欧拉降幂可以得到递推式:
f ( n ) = n f ( n 1 ) % φ ( m ) + φ ( m ) % m o d f(n)=n^{f(n-1)\%\varphi(m)+\varphi(m)}\%mod
接下来我们显然是要求 f ( n 1 ) % φ ( m ) + φ ( m ) f(n-1)\%\varphi(m)+\varphi(m) ,注意到细节,这里的模数已经发生了变化,我本来一直天真的因为模数是不变的,早知道写一下就很容易看出来了
同理:
f ( n 1 ) = ( n 1 ) f ( n 2 ) % φ ( φ ( m ) ) + φ ( φ ( m ) ) % φ ( m ) f(n-1)=(n-1)^{f(n-2)\%\varphi(\varphi(m))+\varphi(\varphi(m))}\%\varphi(m)
f ( n 2 ) = ( n 2 ) f ( n 3 ) % φ ( φ ( φ ( m ) ) ) + φ ( φ ( φ ( m ) ) ) % φ ( φ ( m ) ) f(n-2)=(n-2)^{f(n-3)\%\varphi(\varphi(\varphi(m)))+\varphi(\varphi(\varphi(m)))}\%\varphi(\varphi(m))

什么时候可以退出呢?
首先可以观察到模数mod在不断的变小,直到变到1的时候就可以return了,因为任何数 % 1 \%1 都=0
其次,当n<=4的时候,数字都是可以很容易算出来的,那么也可以特判一下直接return,至于不return会不会超时我也不知道

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (int x=a;x<=b;x++)
#define repp(x,a,b) for (int x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=2e6+7;
const int INF=1e9;
const ll INFF=1e18;
ll ans;
ll phi(ll n)
{
    ll res=n;
    for (int i=2; i*i<=n; i++)
    {
        if (n%i==0)
        {
            res=res/i*(i-1);
            while(n%i==0)
                n/=i;
        }
    }
    if (n>1)
        res=res/n*(n-1);
    return res;
}

ll qpow(ll a,ll b,ll mod)
{
    ll res=1;
    while(b)
    {
        if (b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}
ll solve(ll n,ll mod)
{
    if (mod==1)return 0;
    else if (n==1){return (1%mod);}
    else if (n==2){return (2%mod);}
    else if (n==3){return (9%mod);}
    else if (n==4){return (qpow(4,9,mod));}
    else
    {
        ll p=phi(mod);
        return qpow(n,solve(n-1,p)+p,mod);
    }
}

int main()
{
    ll n,mod;
    scanf("%lld%lld",&n,&mod);
    WW(solve(n,mod));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/w_udixixi/article/details/100739216