D. Mahmoud and Ehab and another array construction task | Codeforces Round #473 (Div. 2)
辞書順最小にしたいので先頭からbの値を決めていく。 とりあえず、aの要素を使える限り使っていく。 例えば、入力例2は
a={10, 3, 7} b={10, 3, 7}
b[i]をa[i]の要素として使えるかどうかを判定するには、あらかじめ十分大きな素数の集合を用意しておく。a[i]を素因数分解して、素数の集合にすべての素因数が含まれているならば可。その場合、その素数の集合から素因数分解で得られた素数を取り除く。
しかし、a[i] = b[i]とできない添字iがあるかもしれない。たとえば、
2 7 8 2 7 9 *
のような場合である。この場合、a[i]より大きい整数を昇順に見ていく。素数でなくてもよいので注意。もし、残った素数の集合で素因数分解できるならば、その値は使える値として最小である。なお、a[i]<=105より、最悪でも105を超える最小の素数まで見ればb[i]を得られる。
このようにa[i]<b[i]となる添字iが存在した場合、残りのj>i, b[j]を構成する方法は簡単。残った素数のリストから小さい順に素数を並べていけばいい。
最後に必要な素数の集合の大きさだが、105以下の素数は104個未満であり、またn<=105より、105+104個ほどの素数があればよい。これには1500000以下の素数を列挙すれば十分。
vector<int> sieve(int n) { vector<bool> f(n + 1); for (int i = 3; i <= n; i += 2) f[i] = 1; for (int i = 3; i <= sqrt(n); i += 2) if (f[i]) for (int j = i * 3; j <= n; j += i) f[j] = 0; vector<int> res; if (n >= 2)res.push_back(2); for (int i = 3; i <= n; i += 2) if (f[i]) res.push_back(i); return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout << fixed << setprecision(20); auto primes = sieve(1500000); set<int> P(all(primes)); int N; cin >> N; vi A(N); rep(i, N)cin >> A[i]; vi ps(100); int gr = 0; rep(i, N) { if (gr) { auto it = P.begin(); cout << *it; P.erase(it); } else { auto factorize = [&](int a) { int m = 0; for (int j = 2; j*j <= a; ++j) { if (a%j == 0) { ps[m++] = j; while (a%j == 0)a /= j; if (!P.count(j)) return -1; } } if (a > 1) { ps[m++] = a; if (!P.count(a))return -1; } return m; }; int m = factorize(A[i]); if (m != -1) { cout << A[i]; rep(j, m) P.erase(ps[j]); } else { gr = 1; int x; for (x = A[i] + 1;; ++x) { m = factorize(x); if (m != -1)break; } cout << x; rep(j, m)P.erase(ps[j]); } } cout << (i != N - 1 ? ' ' : '\n'); } }