mayoko’s diary

プロコンとかいろいろ。

SRM 661 div1 easy:MissingLCM

SRM661には不参加です。一応easyは自力で解けましたが結構時間かかってしまいました。medはこれから解きます。

解法

C++で500ms近くかかっています。想定解とは違うかも。

サンプルにヒントがあるように2*Nという数はキーとなる数字です(答えではないですが)。

なぜならば,

N+1, N+2, ..., 2*N

の中に必ず1, 2, 3, ..., Nに対応する倍数があるからです(例えば2の倍数はN+1かN+2の中に必ずあります。また,5の倍数はN+1, N+2, N+3, N+4,N+5の中に必ずあります。一般にkに対してN+1, N+2, ..., N+kの中に必ずkの倍数があります)。

よって,N+1から2*Nの最小公倍数は,1からNの最小公倍数以上になっていることがわかります。

問題文に「問題の条件を満たすようなMは必ず存在する」と書いてあることから,Mは絶対に2*N以下でなければなりません。

この考察により,Mはたかだか2*Nであることがわかりました。Nは10^6とそこそこ小さいので,全探索することを考えます。

N+1〜Mの最小公倍数が1〜Nの最小公倍数になるためには,各素因数p1, ..., pmについて,p1の指数部,p2の指数部,..., pmの指数部がすべて1〜Nの最小公倍数とN+1〜2*Nの最小公倍数とで同じになっていることが必要十分条件です。

よって,これをそこそこ高速で調べられれば良いです。
最初に

for (int i = 2; i < 2*MAXN; i++) {
    if (prime[i] == 0) {
        for (int j = 2; j * i < 2*MAXN; j++) {
            prime[i*j] = i;
        }
    }
}

と初期化をしておけば,prime[a]にはaが素数の場合には0が入って合成数の場合にはaを割り切る最大の素数が入るので素因数分解はたかだかO(log N)で実行でき,これでaの各素因数の指数を求めることが出来ます。

const int MAXN = 1000100;
int should[2*MAXN];
int prime[2*MAXN];

class MissingLCM {
public:
    int getMin(int N) {
        for (int i = 2; i < 2*MAXN; i++) {
            if (prime[i] == 0) {
                for (int j = 2; j * i < 2*MAXN; j++) {
                    prime[i*j] = i;
                }
            }
        }
        for (int i = 2; i <= N; i++) {
            map<int, int> divs;
            int tmp = i;
            while (prime[tmp]) {
                divs[prime[tmp]]++;
                tmp /= prime[tmp];
            }
            divs[tmp]++;
            for (auto el : divs) {
                should[el.first] = max(should[el.first], el.second);
            }
        }
        int cnt = 0;
        for (int i = 0; i < MAXN; i++) if (should[i]) cnt++;
        for (int M = N+1; ; M++) {
            map<int, int> divs;
            int tmp = M;
            while (prime[tmp]) {
                divs[prime[tmp]]++;
                tmp /= prime[tmp];
            }
            divs[tmp]++;
            for (auto el : divs) {
                if (should[el.first] > 0 && should[el.first] <= el.second) {
                    cnt--;
                    should[el.first] = 0;
                }
            }
            if (cnt == 0) return M;
        }
    }
};