POJ2720_Last Digits_欧拉降幂公式&&打表

题意

定义函数 f(x) = b^f(x-1) if x > 0, and f(0)=1。(a^b表示a的b次幂)
给定 b, i, n,求f(i) 的末n位

思路

首先末 n 位的意思就是 mod 10^n 嘛。所以这个函数就变成了一个同余意义下的递归幂运算了。当然是选择欧拉降幂公式啦。
但是应用欧拉降幂公式是由条件的:指数 e > phi(mod) 。直接套用公式的话,费了好大劲也没能想到怎么能判断这个条件,什么时候用降幂公式,什么时候不用。
打表法
把1e7以下的函数值暴力求出来,并打一个表fbx[b][x]。如果用到这些值,直接返回并取模就可以了。而剩下的没有求出来的,判断一下fbx[b][x-1]是不是大于 phi(mod) ,就可以判断是不是可以用降幂公式了。

另外,对于因为题目说是多组数据,所以每次算出答案后保存一下(mod 1e7)。

链接

http://poj.org/problem?id=2720

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>

using namespace std;

typedef long long LL;

const int maxn = 1e7 + 10;
const int base[8] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000};

int b, x, n;
int fbx[110][110];//保存小于1e7的fb(x)
int tab[110][110];//保存mod1e7的答案

//快速幂
LL quick_pow(int a, int b, int c)
{
    LL res = 1, t = a;

    while(b > 0)
    {
        if(b & 1) res = (res * t) % c;
        t = (t * t) % c;
        b >>= 1;
    }

    return res;
}

//计算phi(C)
LL get_phi(LL C)
{
    LL res = C;

    for(int i= 2; i * i <= C; i++)
    {
        if(C % i == 0)
        {
            res = res / i * (i - 1);
            while(C % i == 0) C /= i;
        }
    }

    if(C > 1) res = res / C * (C - 1);

    return res;
}

int solve(int a, int mod){
    if(fbx[b][a] != -1) return fbx[b][a];//已有结果直接返回
    int phi = get_phi(mod);
    if(fbx[b][a-1] != -1 && fbx[b][a-1] <= phi)//指数较小或不满足欧拉降幂公式的条件
        return quick_pow(b, fbx[b][a-1], mod);//直接快速幂计算
    return quick_pow(b, solve(a-1, phi) + phi, mod);//运用欧拉降幂公式
}

//计算小于1e7的fb(x)
int limit_pow(int a, int b){
    int res = 1;
    for(int i = 0; i < b; ++i){
        res *= a;
        if(res >= base[7]) return -1;
    }
    return res;
}

//初始化 fbx 和 tab数组
void init(){
    memset(tab, -1, sizeof tab);
    memset(fbx, -1, sizeof fbx);
    for(int i = 1; i <= 100; ++i){
        for(int j = 0; j <= 100; ++j){
            tab[i][j] = fbx[i][j] = limit_pow(i, fbx[i][j-1]);
            if(tab[i][j] == -1) break;
        }
    }
}

int main(){
    init();

    while(scanf("%d", &b) == 1 && b){
        if(b == 0) break;
        scanf("%d %d", &x, &n);

        int res;
        if(tab[b][x] == -1){
            tab[b][x] = solve(x, base[7]);//统一mod1e7计算并保存答案
            res = tab[b][x] % base[n];//之后再mod要求的模数
        }else{
            res = tab[b][x] % base[n];
        }

        cout << setfill('0') << setw(n) << res << endl;
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值