POJ 2720 拓展欧拉定理

题意

传送门 POJ 2720

题解

欧拉定理:对于和 m m m 互素的 x x x,有

x φ ( m ) ≡ 1 ( m o d   m ) x^{\varphi(m)}\equiv 1(mod\ m) xφ(m)1(mod m)

拓展欧拉定理:对于 m m m x x x 不一定互质的情况,有

x e   m o d   m = { x e   m o d   m e < φ ( m ) x e   m o d   φ ( m )   +   φ ( m )   m o d   m o t h e r w i s e x^e\ mod\ m=\begin{cases} x^e\ mod\ m&e<\varphi(m)\\ x^{e\ mod\ \varphi(m)\ +\ \varphi(m)}\ mod\ m&otherwise \end{cases} xe mod m={xe mod mxe mod φ(m) + φ(m) mod me<φ(m)otherwise

对于条件的判断依赖于 e e e 的原值大小,而 e e e 可能为取模后回溯的值,处理比较麻烦。手动取一个大于任意 φ ( m ) \varphi(m) φ(m) 的边界即可,这里取 1 e 7 1e7 1e7,则对于大于此边界的值,递归求解;否则,直接取模。

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

#define maxn 101
typedef long long ll;
const int mod = 10000000; 
int B, I, N;
int fx[maxn][maxn], rec[maxn][maxn]; // f(x); 最大模值对应的结果, 较小模值可直接向其取模
int mod_table[8]; // 不同数位的模
int phi[maxn]; // 递归深度对应的欧拉函数
// 快速幂, res >= mod 返回 -1
int pow(int n, int e)
{
    ll res = 1, x = n;
    while (e > 0)
    {
        if (e & 1)
        {
            res = res * x;
            if (res >= mod)
                return -1;
        }
        x = x * x;
        if (x >= mod)
            return -1;
        e >>= 1;
    }
    return res;
}
// 快速幂模运算
ll mod_pow(ll n, ll e, ll mod)
{
    ll res = 1;
    while (e > 0)
    {
        if (e & 1)
        {
            res = res * n % mod;
        }
        n = n * n % mod;
        e >>= 1;
    }
    return res;
}
// 递归求解 f(x) % mod
int dfs(int b, int i, int dep)
{
    int mod = phi[dep];
    if(fx[b][i] == -1){
        return mod_pow(b, dfs(b, i - 1, dep + 1) + mod, mod);
    }
    else{
        return fx[b][i] % mod;
    }
}

void output(int n)
{
    char format[] = "%00d\n";
    format[2] = N + '0';
    printf(format, n);
}

void solve()
{
    if (rec[B][I] != -1)
    {
        output(rec[B][I] % mod_table[N]);
        return;
    }
    int res = dfs(B, I, 0);
    output((rec[B][I] = res) % mod_table[N]);
}
// 欧拉函数
int euler_phi(int n)
{
    int res = n;
    for (int i = 2; i * i <= n; i++)
    {
        if (n % i == 0)
        {
            res = res / i * (i - 1);
            for (; n % i == 0; n /= i)
                ;
        }
    }
    if (n != 1)
        res = res / n * (n - 1);
    return res;
}

void init()
{
    mod_table[0] = 1;
    for (int i = 1; i < 8; i++)
    {
        mod_table[i] = mod_table[i - 1] * 10;
    }
    phi[0] = mod;
    for (int i = 1; i < maxn; i++)
    {
        phi[i] = euler_phi(phi[i - 1]);
    }
    memset(rec, -1, sizeof(rec));
    for (int i = 0; i < maxn; i++)
    {
        rec[i][0] = rec[1][i] = 1;
    }
    memset(fx, -1, sizeof(fx));
    for (int b = 1; b < maxn; b++)
    {
        fx[b][0] = 1;
        for (int i = 1; i < maxn; i++)
        {
            fx[b][i] = pow(b, fx[b][i - 1]);
            if (fx[b][i] == -1)
                break;
        }
    }
}

int main()
{
    init();
    while (~scanf("%d", &B) && B)
    {
        scanf("%d%d", &I, &N);
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值