問題
個の整数が与えられる。「一番小さいものを倍する」という操作を回繰り返した後の整数たちを昇順に並べ出力せよ。
解法
解説PDFの通り。
がソートしてあるとして、ならばそのあと操作される整数はとなる。したがってそれ以降が何回かけられるかは簡単に求めることができる。
になるまでは愚直にシミュレーションするとすると、これは最大値以外のを倍していって最大値を超えるまで操作をするということなので、高々程度の計算量である。最小値の探索にかかったとしても。
シミュレーションが終わったあとは高速な累乗を用いて (ここは解説PDFではとあったが違う気がする。自分の勘違いだろうか)で計算できるので、これでこの問題が解けた。
反省
1時間20分(2WA)でAC。
最初のころはを考えていけば解けるのかと思ったが、最終的に高速累乗を使わなければならないことは目に見えていて、それだと整数型でかける回数を持っていないとダメだということに気づいて頓挫。
よってこれを商と余り、みたいな感じでがを超えない最大のと、そのときの残り部分を持って、というように分解すれば解けそうと思って実装していく。全操作後の最小なについて二分探索をして、必要な最小操作回数がとなるように求めていく方針でいけると思っていた。
1時間経ったくらいタイミングでサンプル1と2は合うものができたが、サンプル3がどうしても合わなず、しばらく格闘したが諦めて解説を見た。
各についての最大と最小が高々1しか差がないことはサンプルの3のデバッグをしているときに気づいたのに、それでシミュレーションすればいいという発想に至らなかった。これはなかなか気づきが必要で難しいように思えるが、問題の考察が足りなかったということだろう。すぐ知っているアルゴリズムに結び付けようとしてしまうが、その前にもっと問題自体の性質を考えなくてはならないなぁ。
コード
#include"bits/stdc++.h" using namespace std; using ll = int64_t; constexpr ll MOD = (ll)1e9 + 7; ll MODpow(ll n, ll m) { ll result = 1; while (m) { if (m % 2 == 1) { result *= n; result %= MOD; } m /= 2; n *= n; n %= MOD; } return result; } int main() { ll N, A, B; cin >> N >> A >> B; vector<ll> a(N); for (ll i = 0; i < N; i++) { cin >> a[i]; } sort(a.begin(), a.end()); if (A == 1) { for (ll e : a) { cout << e % MOD << endl; } return 0; } while (true) { auto min_itr = min_element(a.begin(), a.end()); if (*min_itr * A >= *max_element(a.begin(), a.end())) { break; } *min_itr *= A; if (--B == 0) { sort(a.begin(), a.end()); for (ll e : a) { cout << e % MOD << endl; } return 0; } } sort(a.begin(), a.end()); for (ll i = B % N; i < N; i++) { cout << a[i] * MODpow(A, B / N) % MOD << endl; } for (ll i = 0; i < B % N; i++) { cout << a[i] * MODpow(A, B / N + 1) % MOD << endl; } }