0w1

POJ 2720 Last Digits

Problem Description:
f(x) = 1, if x = 0
B ** f(x - 1), otherwise
Given B, I, and N, find the last N digits of f(I).

Constraints:
 {1 ≤ B, I ≤ 100}
 {1 ≤ N ≤ 7}

Solution:
We want to find  {\displaystyle B^{f(I - 1)}\ modulo\ 10^7}.
For any  {B}, we can observe that in  {B,\ B^2,\ B^3,\ ...}, we can always find some  {k,\ k < m} such that  {B^{k + i}\equiv\ B^{k + i + t * \phi(m)}\ \pmod{m},\ for\ any\ positive\ integer\ t\ and\ nonnegative\ integer\ i}.
We will recursively compute the answer, i.e. from the most top value to the bottom.
Note that  {a^b\equiv\ a^{b\ modulo\ \phi(m)},\ iff\ b < \phi(m)}, and  {a^b\equiv\ a^{\phi(m) + (b\ modulo\ \phi(m)},\ otherwise}.
Since  {m ≤ 1e7}, we will keep track whether at some moment the power has exceeded  {1e7}. When such situation occurs, all later calculations only involve the second case, otherwise, honestly compute the first case without applying modulo.

Code:

#include <iostream>
#include <algorithm>
#include <string>
#include <sstream>
#include <cstring>
#include <cmath>

const int MAXB = 100;
const int MAXI = 100;
const int MAXN = 7;
const int MOD = 1e7;

int B, I, N;

int ten[MAXN + 1];
int i_phi[MAXI + 1]; // interesting phi's
int bp[MAXB + 1][25][MAXI + 1];
int dp[MAXB + 1][MAXI + 1][MAXI + 1];
int gp[MAXB + 1][MAXI + 1][MAXI + 1];

int phi(int x) {
  int r = x;
  for (int i = 2; i * i <= x; ++i) {
    if (x % i == 0) {
      r = r / i * (i - 1);
      while (x % i == 0) x /= i;
    }
  }
  if (x != 1) r = r / x * (x - 1);
  return r;
}

int mod_pow(int v, int p, int mi) {
  if (i_phi[mi] == 1) return 0;
  if (v == 1 || p == 0) return 1;
  int r = 1;
  for (int i = p; i; i &= i - 1) {
    r = 1LL * r * bp[v][__builtin_ctz(i)][mi] % i_phi[mi];
  }
  return r;
}

int int_pow(int v, int p) {
  int r = 1;
  while (p--) r *= v;
  return r;
}

void solve(int b, int p, int mi) {
  if (~dp[b][p][mi]) return;
  if (i_phi[mi] == 1) return dp[b][p][mi] = 0, void();
  if (b == 1 || p == 0) return dp[b][p][mi] = 1, void();
  solve(b, p - 1, mi + 1);
  int q;
  if (gp[b][p - 1][mi + 1]) {
    q = dp[b][p - 1][mi + 1] + i_phi[mi + 1];
    dp[b][p][mi] = mod_pow(b, q, mi);
    gp[b][p][mi] = 1;
  } else {
    q = dp[b][p - 1][mi + 1];
    if (log10(b) * q >= 7) {
      dp[b][p][mi] = mod_pow(b, q, mi);
      gp[b][p][mi] = 1;
    } else {
      dp[b][p][mi] = int_pow(b, q);
      gp[b][p][mi] = 0;
    }
  }
}

signed main() {
  for (int i = ten[0] = 1; i <= MAXN; ++i) {
    ten[i] = ten[i - 1] * 10;
  }
  i_phi[0] = MOD;
  for (int i = 1; i <= MAXI; ++i) {
    i_phi[i] = phi(i_phi[i - 1]);
  }
  for (int i = 2; i <= MAXB; ++i) {
    for (int j = 0; j + 1 < 25; ++j) {
      for (int k = 0; k <= MAXI; ++k) {
        if (j == 0) bp[i][j][k] = i % i_phi[k];
        bp[i][j + 1][k] = 1LL * bp[i][j][k] * bp[i][j][k] % i_phi[k];
      }
    }
  }
  std::memset(dp, 0xff, sizeof(dp));
  std::ios::sync_with_stdio(false);
  while (std::cin >> B >> I >> N) {
    solve(B, I, 0);
    int ans = dp[B][I][0] % ten[N];
    int d = 0;
    while (d < N && ans >= ten[d]) ++d;
    for (int i = 0; i + d < N; ++i) {
      std::cout << 0;
    }
    if (ans) std::cout << ans;
    std::cout << '\n';
  }
  return 0;
}