题意
定义函数 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;
}