Strorkisのブログ

信憑性については悪しからず

ABC068D

D - Decrease (Contestant ver.)

長さ Nの数列において、最大値の値を N減らすという操作を K回行うことにより、最大値が N-1になるような数列を求めよという問題です。


 a_iに厳しめの制約がある為、 N=50の場合を考えます。

私は、 Kが変化した場合に何を変化させれば良いのかを K=0から順に考えました。


特に記述がない限り、 a_i = 0とします。

 K=0の場合、 a_0 = 49です。
最初から最大値が N-1である必要があります。

 K=1の場合、 a_0 = 99、または、 50 \leq a_0 < 99かつ a_1 = 48です。
 a_0 = 99の場合は、 a_0 - Nによって最大値が N-1になります。
 50 \leq a_0 < 99かつ a_1 = 48の場合は、 a_1 + 1によって最大値が N-1になります。
 a_iの制約を考えると、後者の方が良さそうです。


同じように考えると、 K=2の場合、  a_0 = 50, a_1 = 49, a_2 = 47であれば良いことがわかります。

つまりは、49から0までの数列を作っておき、左側からKの値だけインクリメントして行けば良さそうです。


では、一番右まで行ってしまった場合、つまり K=51の場合はどうすれば良いでしょうか。

結論としては、 K=50の時点で、 a_0=a_0-N+(N-1)となっているため、 a_0をインクリメントすれば良いです。

つまりは、一番左に戻れば良いという事ですね。


ただ、1ずつインクリメントするのは明らかにTLEするので、実装では各 a_iにおいて、 K \div Nを足した後、 K\ mod\ Nだけ左側からインクリメントしています。

/* typedef long long ll; */

  ll k; cin >> k;
  int n = 50;
  vector<ll> a(n);
  for (int i = 0; i < n; i++) a[i] = n - 1 - i + k / n;
  for (int i = 0; i < k % n; i++) a[i]++;

  cout << n << endl << a.front();
  for (int i = 1; i < n; i++) cout << " " << a[i];
  cout << endl;


説明難しい。