mayoko’s diary

プロコンとかいろいろ。

Codeforces Round #213 (Div. 1) C. Beautiful Set

解法

適当に書いたら通ってしまったという感じ(解説を見ると writer もいろいろ解法ありそうと思っていたようです)なので証明も何もないんですが, やったことを書きます。

素数の limit 番目まで使う, と決めた時, どのような整数集合がいい感じかを考えます。
例えば, 2, 3, 5 を使うとした時, 2*3*5 = 30 に対して 2, 3, 5 をいくつか掛けたやつは, 必ず 2 でも 3 でも 5 でも割れるので有利です。次に考えたいのは, 3*5 = 15 に対して 3, 5 をいくつか掛けたやつ, 次は (2, 5) の組み合わせ, ... みたいな感じです(2*3*5 の組み合わせだけは間違いなく最初にやった方が良いですが, あとはなんとなくそのほうが良い, というだけでなんでこれで上手く行くのか説明は出来ません)。

こんな感じで, s = (2^limit)-1 から s = 1 まで順番に, 整数集合を増やしていくことにします。

できた集合が条件を満たしていればそれを出力したら AC 出来ました。

int prime[15] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};

set<int> solve(int limit, int k) {
    set<int> ret;
    for (int s = (1<<limit)-1; s > 0; s--) {
        int p = 1;
        bool ng = false;
        for (int i = 0; i < 15; i++) {
            if ((s>>i)&1) {
                p *= prime[i];
                if (p > 2*k*k) {
                    ng = true;
                    break;
                }
            }
        }
        if (ng) continue;
        queue<int> que;
        que.push(p);
        while (!que.empty() && ret.size() < k) {
            int now = que.front(); que.pop();
            ret.insert(now);
            for (int i = 0; i < 15; i++) {
                if ((s>>i)&1) {
                    int next = now*prime[i];
                    if (next <= 2*k*k) que.push(next);
                }
            }
        }
        if (ret.size() == k) return ret;
    }
    return ret;
}

int cnt[15];

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    int k;
    cin >> k;
    for (int i = 1; i < 15; i++) {
        set<int> S = solve(i, k);
        if (S.size() < k) continue;
        memset(cnt, 0, sizeof(cnt));
        for (int el : S) {
            for (int i = 0; i < 15; i++) if (el%prime[i] == 0) cnt[i]++;
        }
        bool ng = false;
        for (int i = 0; i < 15; i++) {
            if (cnt[i] != 0 && 2*cnt[i] < k) ng = true;
        }
        if (ng) continue;
        for (int el : S) {
            cout << el << " ";
        }
        cout << endl;
        break;
    }
    return 0;
}