結果

問題 No.2829 GCD Divination
ユーザー InTheBloomInTheBloom
提出日時 2024-08-03 01:04:59
言語 C++23(gcc13)
(gcc 13.2.0 + boost 1.83.0)
結果
AC  
実行時間 119 ms / 2,000 ms
コード長 2,539 bytes
コンパイル時間 1,431 ms
コンパイル使用メモリ 126,784 KB
実行使用メモリ 6,944 KB
最終ジャッジ日時 2024-08-03 01:05:03
合計ジャッジ時間 3,390 ms
ジャッジサーバーID
(参考情報)
judge2 / judge1
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
6,812 KB
testcase_01 AC 2 ms
6,940 KB
testcase_02 AC 2 ms
6,940 KB
testcase_03 AC 1 ms
6,940 KB
testcase_04 AC 3 ms
6,944 KB
testcase_05 AC 119 ms
6,940 KB
testcase_06 AC 101 ms
6,940 KB
testcase_07 AC 85 ms
6,944 KB
testcase_08 AC 77 ms
6,940 KB
testcase_09 AC 60 ms
6,940 KB
testcase_10 AC 1 ms
6,944 KB
testcase_11 AC 2 ms
6,940 KB
testcase_12 AC 2 ms
6,940 KB
testcase_13 AC 2 ms
6,940 KB
testcase_14 AC 1 ms
6,940 KB
testcase_15 AC 1 ms
6,940 KB
testcase_16 AC 2 ms
6,944 KB
testcase_17 AC 2 ms
6,940 KB
testcase_18 AC 2 ms
6,940 KB
testcase_19 AC 1 ms
6,940 KB
testcase_20 AC 1 ms
6,944 KB
testcase_21 AC 2 ms
6,944 KB
testcase_22 AC 2 ms
6,940 KB
testcase_23 AC 3 ms
6,940 KB
testcase_24 AC 1 ms
6,940 KB
testcase_25 AC 2 ms
6,944 KB
testcase_26 AC 2 ms
6,940 KB
testcase_27 AC 2 ms
6,940 KB
testcase_28 AC 1 ms
6,940 KB
testcase_29 AC 2 ms
6,940 KB
testcase_30 AC 2 ms
6,944 KB
testcase_31 AC 1 ms
6,940 KB
testcase_32 AC 1 ms
6,940 KB
testcase_33 AC 1 ms
6,940 KB
testcase_34 AC 1 ms
6,940 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <map>
#include <cassert>

using namespace std;

vector<int> divisors (int N) {
    vector<int> res;
    for (int i = 1; i <= N; i++) {
        if (N < 1LL * i * i) break;
        if (N % i == 0) {
            res.push_back(i);
            if (1LL * i * i < N) res.push_back(N / i);
        }
    }

    sort(res.begin(), res.end());
    return res;
}

int main () {
    int N; cin >> N;
    // いつものお気持ちdpで立式は行ける。あとはあるxに対してE(gcd(x, v))の総和をとるパートをどうにかしたい。
    // gcd(x, v)の値で主客転倒して考えてみると、約数系除原理でgcd(x, v) = xからはじめて後ろ向きに求まる。
    // これであるxに対してO(sqrt(x) + (約数の個数)^2)で次のステップに進める。
    // さて、xの約数の約数もxの約数であるため、sqrt(x)で計算した分はつかいまわせる。また、「次のステップ」の最悪ケースは一つ小さな約数に行くことであるから、結局O((約数の個数)^3)で抑えることが出来て、全体でO(sqrt(x) + (約数の個数)^3)
    // 補足: xの約数dに対して、gcd(x, v) = dの必要条件はvがdの倍数であること。すなわち上限はfloor(x / d)個。ここからd < gcd(x, v)なる場合の数を除けばよい。

    auto div = divisors(N);

    map<int, double> memo;
    auto E = [&] (auto self, int x) -> double {
        if (memo.find(x) != memo.end()) return memo[x];
        if (x == 1) return 0;
        double res = 1;

        // gcd(x, v)の値で主客転倒
        int index = 0;
        while (div[index] < x) index++;
        map<int, int> mp;

        for (int i = index; 0 <= i; i--) {
            // ここのcontinueがないとダメ(N未満の数に対してはすべての場所が約数になるとは限らないので)
            if (x % div[i] != 0) continue;
            mp[div[i]] = x / div[i];

            // 約数系除原理
            for (int j = 1; i + j < div.size(); j++) {
                if (div[i + j] % div[i] == 0 && mp.find(div[i + j]) != mp.end()) {
                    mp[div[i]] -= mp[div[i + j]];
                }
            }
        }

        for (auto it : mp) {
            if (it.first == x) continue;
            res += (1 + self(self, it.first)) * it.second;
        }

        return memo[x] = res / (x - 1);
    };

    cout << setprecision(10);
    cout << E(E, N) << "\n";
}

0