广义高阶幂取模(模板向)

高阶幂,或者叫幂塔,指的是一个数幂次的幂次的幂次……,也就是 a a a . . . a^{a^{a...}} aaa...
而广义高阶幂则指幂塔上存在不同的数,形如 a 0 a 1 a 2 . . . a_0^{a_1^{a_2...}} a0a1a2...

高阶幂取模典型题目:CodeForces 906D Power Tower

题目大意:就是上述的广义高阶幂去取模:给你n个数,q次查询,每次给定一个l和r,查询这n个数中由区间 [ a l , a r ] [a_l, a_r] [al,ar]所组成的幂塔去模m的结果。

思路:欧拉降幂。
我们知道欧拉降幂的公式 n x ≡ n x   m o d   ϕ ( m )   +   ϕ ( m )   ( m o d   m ) i f   x ≥ ϕ ( m ) n^x \equiv n^{x \space mod \space \phi(m) \space + \space \phi(m) } \space (mod \space m)\quad if \space x \geq \phi(m) nxnx mod ϕ(m) + ϕ(m) (mod m)if xϕ(m)
所以我们自定义一个模运算:

#define Mod(a,b) a<b?a:a%b+b

使公式变成: n x ≡ n M o d ( x ,   ϕ ( m ) ) ( m o d   m ) n^x \equiv n^{Mod(x, \space \phi(m))}(mod \space m) nxnMod(x, ϕ(m))(mod m),该公式就在x为任意非负整数下适用了。
如此,幂塔就可以递归地将降幂进行下去,递归的过程会将φ函数迭代下去:
a 0 a 1 a 2 a 3 . . .   m o d   m = a 0 M o d ( a 1 a 2 a 3 . . . , ϕ ( m ) )   m o d   m = a 0 M o d ( a 1 M o d ( a 2 a 3 . . . , ϕ ( ϕ ( m ) ) ) , ϕ ( m ) )   m o d   m . . . . . . a_0^{a_1^{a_2^{a_3...}}}\space mod\space m\\=a_0^{Mod(a_1^{a_2^{a_3...}}, \phi(m))}\space mod\space m \\ = a_0^{Mod(a_1^{Mod(a_2^{a_3...},\phi(\phi(m)))},\phi(m))}\space mod \space m\\ ...... a0a1a2a3... mod m=a0Mod(a1a2a3...,ϕ(m)) mod m=a0Mod(a1Mod(a2a3...,ϕ(ϕ(m))),ϕ(m)) mod m......

代码:

#include<bits/stdc++.h>
#define Mod(a,b) a<b?a:a%b+b						//重定义取模,按照欧拉定理的条件
#define LL long long
#define N 100010
using namespace std;
 
LL n,q,mod,a[N];
map<LL,LL> mp;
 
LL qpow(LL x,LL n,LL mod)
{
    LL res=1;
    while(n)
    {
        if (n&1) res=Mod(res*x,mod),n--;
        x=Mod(x*x,mod); n>>=1;
    }
    return res;
}
 
LL phi(LL k)
{
    LL i,s=k,x=k;
    if (mp.count(k)) return mp[x];					//记忆化存储
    for(i = 2;i * i <= k; i++)
    {
        if(k % i == 0) s = s / i * (i - 1);
        while(k % i == 0) k /= i;
    }
    if(k > 1) s = s / k * (k - 1);
    mp[x]=s; return s;
}
 
LL solve(LL l,LL r,LL mod)
{
    if (l==r||mod==1) return Mod(a[l],mod);					//如果到右端点或者φ值等于1,那么直接返回当前数字
    return qpow(a[l],solve(l+1,r,phi(mod)),mod);			//否则指数为[l+1,r]区间的结果
}
 
int main()
{
    scanf("%lld%lld",&n,&mod);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    scanf("%lld",&q);
    while(q--)
    {
        int L,R;
        scanf("%d%d",&L,&R);
        printf("%lld\n",solve(L,R,mod)%mod);				//对mod取模,因为qpow内部是用Mod(a,b)取模
    }
    return 0;
}

你可能感兴趣的:(原创,数论学习)