問題概要
あるバクテリアは、現在x個あったとすると1時間後にはxx個に増えるという性質をもつ。もともとB個いたバクテリアがA時間後には何個になっているかを求めよ。ただし、とても大きな数なのでCで割った余りを求めよ。1 ≦ A, B, C ≦ 1000
方針
べき乗の部分は、先に余りを求めて計算してはいけないことに注意。Cが高々1000なので、鳩ノ巣原理によって任意の正の整数pのべき乗を並べたものは最高でも1000回以内にループに入る。ループに入るまでの長さと、ループの1サイクルの周期を予め計算しておく。あとはこの情報を利用して再帰的に解く。実装は結構ややこしかった。printfデバッグがうまくなった気がする。ソースコード
- const int MAX_N = 1000;
- const long long INF = 10000;
- int head[MAX_N+1][MAX_N+1];
- int cycle[MAX_N+1][MAX_N+1];
- int dp[MAX_N+1][MAX_N+1];
- int val[MAX_N+1];
- void init() {
- int used[MAX_N+1];
- for (int i = 1; i <= MAX_N; i++) {
- for (int j = 1; j <= MAX_N; j++) {
- memset(used, -1, sizeof(used));
- int p = 0;
- int x = 1;
- while (1) {
- x = x * i % j;
- if (used[x] >= 0) {
- head[i][j] = used[x];
- cycle[i][j] = p - used[x];
- break;
- }
- used[x] = p++;
- }
- }
- }
- cerr << "init() done!" << endl;
- }
- long long power(long long x, long long b, long long mod) {
- long long ret = 1LL;
- while (b > 0) {
- if (b & 1)
- ret = ret * x % mod;
- x = x * x % mod;
- b >>= 1;
- }
- return ret;
- }
- long long power(long long x, long long b) {
- long long ret = 1LL;
- while (b > 0) {
- if (b & 1)
- ret = min(INF, ret*x);
- x = min(INF, x*x);
- b >>= 1;
- }
- return ret;
- }
- int rec(int x, int b, int mod) {
- if (!b) return x % mod;
- if (mod == 1) return 0;
- if (dp[b][mod] >= 0) return dp[b][mod];
- int y = rec(x, b-1, mod);
- if (!y)
- return dp[b][mod] = 0;
- int p = head[y][mod];
- int q = cycle[y][mod];
- int ret;
- if (val[b-1] <= p)
- ret = power(y, val[b-1], mod);
- else {
- int z = rec(x, b-1, q);
- z = ((z - (p+1)) % q + q) % q + 1;
- ret = power(y, p+z, mod);
- }
- return dp[b][mod] = ret;
- }
- void solve() {
- int T;
- cin >> T;
- for (int i = 0; i < T; i++) {
- cerr << "solving case " << i+1 << endl;
- int A, B, C;
- cin >> A >> B >> C;
- val[0] = A;
- for (int j = 1; j <= MAX_N; j++)
- val[j] = power(val[j-1], val[j-1]);
- memset(dp, -1, sizeof(dp));
- int ret = rec(A, B, C);
- cout << "Case #" << i+1 << ": " << ret << endl;
- }
- }
- #define SUBMIT
- int main() {
- #ifdef SUBMIT
- freopen("./test.in", "r", stdin);
- freopen("./test.out", "w", stdout);
- #endif
- init();
- solve();
- #ifdef SUBMIT
- fflush(stdout);
- cerr << "all done!" << endl;
- #endif
- return 0;
- }
0 件のコメント:
コメントを投稿