はまやんはまやんはまやん

hamayanhamayan's blog

バラバラ掛け算 [技術室奥プログラミングコンテスト#4 Day1 G]

https://atcoder.jp/contests/tkppc4-1/tasks/tkppc4_1_g

解説

https://atcoder.jp/contests/tkppc4-1/submissions/6664457

制約に弱点がないので、なにか特殊な性質があるのだろうと考察する。
まずは実験してみよう。

ll dp[30];
void labo() {
    dp[0] = 0;
    dp[1] = 1;
    rep(i, 2, 30) {
        dp[i] = i;
        rep(j, 0, i) chmax(dp[i], dp[j] * dp[i - j]);
    }

    rep(i, 0, 30) printf("dp[%d] = %lld\n", i, dp[i]);
}

結果は

dp[0] = 0
dp[1] = 1
dp[2] = 2
dp[3] = 3
dp[4] = 4
dp[5] = 6
dp[6] = 9
dp[7] = 12
dp[8] = 18
dp[9] = 27
dp[10] = 36
dp[11] = 54
dp[12] = 81
dp[13] = 108
dp[14] = 162
dp[15] = 243
dp[16] = 324
dp[17] = 486
dp[18] = 729
dp[19] = 972
dp[20] = 1458
dp[21] = 2187
dp[22] = 2916
dp[23] = 4374
dp[24] = 6561
dp[25] = 8748
dp[26] = 13122
dp[27] = 19683
dp[28] = 26244
dp[29] = 39366

これを見ると、なるべく3を作って、余ったら2にする方針みたい。
N % 3 == 0ならば、333...のようにする
N % 3 == 1ならば、2を2つ作る。2
2333...
N % 3 == 2ならば、2を1つ作る。233*...

あとは、小さいケースにコーナーがあるので気をつけよう。

ll dp[30];
void labo() {
    dp[0] = 0;
    dp[1] = 1;
    rep(i, 2, 30) {
        dp[i] = i;
        rep(j, 0, i) chmax(dp[i], dp[j] * dp[i - j]);
    }

    rep(i, 0, 30) printf("dp[%d] = %lld\n", i, dp[i]);
}
//---------------------------------------------------------------------------------------------------
mint solve(ll N) {
    if (N % 3 == 0) {
        if (N == 0) return 0;
        return mint(3) ^ (N / 3);
    }
    else if (N % 3 == 1) {
        if (N == 1) return 1;
        return mint(2) * mint(2) * (mint(3) ^ ((N - 4) / 3));
    }
    else return mint(2) * (mint(3) ^ ((N - 2) / 3));
}
//---------------------------------------------------------------------------------------------------
void _main() {
    int Q; cin >> Q;
    rep(q, 0, Q) {
        ll N; cin >> N;
        mint ans = solve(N);
        printf("%d\n", ans.get());
    }
}