結果

問題 No.14 最小公倍数ソート
ユーザー codershifthcodershifth
提出日時 2015-07-20 11:05:59
言語 C++11
(gcc 11.4.0)
結果
AC  
実行時間 52 ms / 5,000 ms
コード長 5,114 bytes
コンパイル時間 1,419 ms
コンパイル使用メモリ 160,816 KB
実行使用メモリ 8,864 KB
最終ジャッジ日時 2023-09-22 20:33:15
合計ジャッジ時間 3,024 ms
ジャッジサーバーID
(参考情報)
judge12 / judge14
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
4,380 KB
testcase_01 AC 1 ms
4,376 KB
testcase_02 AC 2 ms
4,380 KB
testcase_03 AC 6 ms
4,388 KB
testcase_04 AC 52 ms
8,840 KB
testcase_05 AC 26 ms
6,484 KB
testcase_06 AC 29 ms
6,668 KB
testcase_07 AC 36 ms
7,324 KB
testcase_08 AC 42 ms
7,908 KB
testcase_09 AC 51 ms
8,452 KB
testcase_10 AC 49 ms
8,204 KB
testcase_11 AC 50 ms
8,524 KB
testcase_12 AC 50 ms
8,652 KB
testcase_13 AC 50 ms
8,580 KB
testcase_14 AC 49 ms
8,728 KB
testcase_15 AC 51 ms
8,864 KB
testcase_16 AC 29 ms
6,792 KB
testcase_17 AC 24 ms
6,156 KB
testcase_18 AC 16 ms
5,408 KB
testcase_19 AC 39 ms
7,732 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <bits/stdc++.h>

typedef long long ll;
typedef unsigned long long ull;

#define FOR(i,a,b) for(int (i)=(a);i<(b);i++)
#define REP(i,n) FOR(i,0,n)
#define RANGE(vec) (vec).begin(),(vec).end()

using namespace std;

template<typename T>
T gcd(T a, T b) {
    if ( a < b )
        std::swap(a,b);
    if ( b == 0 )
        return a;
    return gcd(b, a%b);
}
template<typename T>
inline T lcm(T a, T b) {
        return a*b/gcd(a,b);
}

class LCMsort {
public:
    // O(N*N*log(A))
    // 速度的にぎりぎりの解法
    void solve1(void) {
            int N;
            cin>>N;
            vector<int> a(N,0);
            REP(i,N)
                cin>>a[i];

            // sort を進めるにつれソート対象列が短くなるので、実際にソートするのでなく、lcm が最も小さい
            // ものを取り出していくだけでよい。
            // O(N^2*log(A))
            REP(pivot,N)
            {
                cout<<a[pivot]<<" ";
                int mx = (1<<30);
                int mi = -1;
                // O(N*log(A)) pivot が更新されるにつれこのループは高速化されていく。
                // (N-1) -> (N-2) -> ... -> (1)
                FOR(i, pivot+1, N)
                {
                    int k = lcm(a[pivot], a[i]);
                    if (k < mx || (k == mx && a[i] < a[mi]))
                    {
                        mx = k;
                        mi = i;
                    }
                }
                if (mi < 0)
                    break;
                // pivot を入れ替えることで FOR(i, pivot+1, N) のループ回数を減らせる
                swap(a[pivot+1], a[mi]);
            }
            cout<<endl;
    }
    // O(√N*N*log(A))
    void solve_optim(void) {
            int N;
            cin>>N;
            vector<int> a(N,0);
            int maxA = 0;
            REP(i,N)
            {
                cin>>a[i];
                maxA = max(a[i], maxA);
            }

            // 前計算として約数 -> index のマップを作っておく
            vector<vector<int>>        div(N+1);  // div[i] := a[i] の約数リスト
            vector<set<pair<int,int>>> S(maxA+1); // S[d] := 約数 d を持つ a[i] のリスト
                                                  // pair にしているのは a[i] が小さい順にソートしておきたいから
                                                  // second に i を入れることで a[i]==a[j] なら
                                                  // i<j なるものを選択されるようにしている。
            // O(sqrt(maxA)*N*log(maxA))
            REP(i,N)
            {
                int x = a[i];
                // O(sqrt(maxA)) loop
                for (int d = 1; d*d <= x; ++d)
                {
                    if (x%d == 0)
                    {
                        div[i].push_back(d);
                        if (d != x/d)
                            div[i].push_back(x/d);
                        // 各約数ごとに元になった数字と index を格納
                        // O(log(maxA))
                        S[d].emplace(x,i);
                        S[x/d].emplace(x,i);
                    }
                }
            }

            int pivot = 0;
            REP(_,N)
            {
                cout<<a[pivot]<<" ";
                int mx = (1<<30);
                int mi = -1;
                // O(sart(maxA)*log(maxA))
                // a[pivot] の約数ごとに捜査する
                for (auto d : div[pivot])
                {
                    // 自分自身を参照しないように削除しておく(今後も参照されないはず)
                    S[d].erase(make_pair(a[pivot], pivot));
                    if (S[d].empty())
                        continue;
                    // 先頭の組み合わせを取得
                    // 約数 d を持つ a[i] のうち最小のものをとる
                    // pivot として出現しているものは上の erase で消えているので見つからないはず。
                    // (リスト数列は d 以外に約数を持たないとして考えてよいので)
                    // d*b1 <= d*b2 < ... なので lcms = x*b1 <= x*b2 <= ... となる。(x=a[pivot])
                    // よって最小の (aa,ii) だけとりだせばよい。
                    int aa, ii;
                    tie(aa,ii) = *S[d].begin();
                    int k = lcm(a[pivot], aa);
                    if (k < mx || (k == mx && a[ii] < a[mi]))
                    {
                        mx = k;
                        mi = ii;
                    }
                }
                if (mi < 0)
                    break;
                pivot = mi;
            }
            cout<<endl;
    }
    void solve(void) {
            solve_optim();
    }
};

#if 1
int main(int argc, char *argv[])
{
        ios::sync_with_stdio(false);
        auto obj = new LCMsort();
        obj->solve();
        delete obj;
        return 0;
}
#endif
0