Google Code Jam Japan 2011 本戦 B. バクテリアの増殖
B : バクテリアの増殖
- この問題を解くための基本アイディアとして
- n時間後のバクテリア数を x(n) とする
- ( x(n)^x(n) )%C = ( (x(n)%C)^x(n) )%C である
- ((x(n)%C)^p)%C = 0 となるpがなければ、x(n)%C のべき乗は値が循環する
- その循環の長さを y とすると ( (x(n)%C)^x(n) )%C = ( (x(n)%C)^(y+x(n)%y) )%C
- 何乗かしてからサイクルに入る場合もあるので、1周分以上は計算する
- まとめると、x(n+1)%C を求めるには x(n)%C、サイクル長y、x(n)%y が必要
- x(n)を1〜Cで割った余りを求めておけば、x(n+1)を1〜Cで割った余りが求められる
- サイクル長はオイラーのφ関数によって求めておける
- x(1)を1〜Cで割った余りは単純に計算して求めておける
- これを使って、上記によりx(2), x(3),... について求めていけば良い
- また、2^10 > 1000 なので、A^10がCで割り切れなければ答えは0にならない
書いてみたコード
#include <iostream> using namespace std; int cycle[1001]; int modPow(int A, int p, int C){ if(p==0) return 1; int res = modPow(A, p/2, C); res = (res*res)%C; if(p%2 == 1) res = (res*A)%C; return res; } int solveLarge(int A, int B, int C){ // 答えが0にならないかチェック int t = modPow(A, 10, C); if(B >= 2 && t == 0){ if(A == 2 && B == 2 && C > 256) return 256; return 0; } int res[2][1001]; for(int i=1;i<=C;i++) res[0][i] = modPow(A, A, i); int cur = 0, next = 1; for(int i=2;i<=B;i++){ res[next][1] = 0; for(int j=2;j<=C;j++){ int c = cycle[j]; int p = res[cur][c]; res[next][j] = modPow(res[cur][j], c+p, j); } swap(cur, next); } return res[cur][C]; } int main(){ int TEST; cin >> TEST; // オイラーのφ関数で周期を求めておく for(int i=1;i<=1000;i++) cycle[i] = i; for(int i=2;i<=1000;i++){ if(cycle[i] != i) continue; for(int j=i;j<=1000;j+=i) cycle[j] = cycle[j]/i*(i-1); } for(int test=1;test<=TEST;test++){ int A, B, C; cin >> A >> B >> C; printf("Case #%d: %d\n", test, solveLarge(A, B, C)); } }