Exponial Kattis - exponial(欧拉降幂)

降幂公式:
在这里插入图片描述
模板题:
Exponial .
计算:exponial(n)%m . (1<= n,m <= 109)
exponial(n) = n^ (n-1^ ( n-2^ ( n-3^ (n-4^ (…^ 1))))

详情看代码注释

#include<bits/stdc++.h>
using namespace std;
#define inf (long long)0x3f3f3f3f
long long tmp;
long long euler(long long n)//求一个数的欧拉函数
{
    long long ans=n;
    for(long long i=2; i*i<=n; i++)
    {
        if(n%i==0)
        {
            ans-=ans/i;
            while(n%i==0)
                n/=i;
        }
    }
    if(n>1)
        ans -= ans/n;
    return ans;
}


long long pow2(long long a,long long n)//tmp的快速幂
{
    long long ans = 1;
    while(n)
    {
        if(n&1)
            ans = min(inf,ans*a);
        a = min(a*a,inf);
        n>>=1;
    }
    return ans;
}

long long qui_pow(long long a,long long n,long long mod)//普通快速幂
{
    long long ans =1;
    while(n)
    {
        if(n&1)
            ans = ans*a%mod;
        a = a*a%mod;
        n>>=1;
    }
    return ans;
}
//降幂公式
//A^B mod(C) = A^(B mod euler(C)) mod C            when (B < euler(C))
//A^B mod(C) = A^(B mod euler(C)+euler(C)) mod C   when (B >= euler(C))

long long dfs(long long n,long long mod)//每次的模数为上一次的欧拉函数,所以最多调用log(n)次递归函数,
{
    if(mod==1||n==1)//本次递归的模数是1或者递归到n=1,可以得到结果
    {
        tmp = 1;
        return 1%mod;//当mod==1时返回结果一定是0,因为任何数模1都是0; 当n==1时,返回结果是1%mod
    }
    long long ans;
    long long exp = dfs(n-1,euler(mod));//幂层递归的模数是本层模数的欧拉函数
    if(tmp>=euler(mod))
        ans = qui_pow(n,exp+euler(mod),mod)%mod;//这里和下面exp没有像公式里的模euler(mod),是因为 (上一层的mod)==(这一层的euler(mod)),已经在上一层模完了
    else
        ans = qui_pow(n,exp,mod)%mod;
        
    tmp = min(inf,pow2(n,tmp));//tmp是记录的真实的,不被模的幂,以此判断使用哪个降幂公式,取一个比最大的模数大的数inf作为tmp的最大值,以免乘爆
    return ans;

}

int main()
{
    long long num;
    cin>>num;
    print(num);
    long long i,j,m,n,mod;
    ios::sync_with_stdio(false);
    while(scanf("%lld %lld",&n,&mod)!=EOF)
    {
        cout<<dfs(n,mod)<<endl;
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值