Official

E - Arithmetic Number Editorial by physics0523


この問題で最も重要な事実は、「\(10^{18}\) 未満の等差数はそう多くない」ということです。まずはこのことを見積もりましょう。
等差数のもとになる等差数列 (例えば \((2,4,6,8) \rightarrow 2468\)) の候補を考えます。 等差数列が満たすべき必要条件は以下です。

  • 初項が \(1\) 以上 \(9\) 以下である。
  • 公差が \(-9\) 以上 \(8\) 以下である。
  • 数列の長さは \(1\) 以上 \(18\) 以下である。

(長さ \(1\) の数列を重複して数えたとしても、) この条件を満たす数列は \(9 \times 18 \times 18 = 2916\) 個以下であることがわかります。 ただし、この条件を満たしていても、 \((7,1,-5)\) のような数列は等差数のもととはなりません。追加で以下の条件が必要です。

  • \(2\) 項以降が全て \(0\) 以上 \(9\) 以下である。

上に挙げた \(4\) つの条件全てを満たせば十分です。あとは、これをもとに等差数を全列挙し、 \(X\) 以上であるものの中で最小のものを出力すればよいです。なお、 \(X=10^{17}\) である場合、答えは \(111111111111111111\) (\(1\)\(18\) 個並んだ数) なので、 \(10^{18}\) 以上の等差数は考慮に入れる必要がありません。

この解法は、等差数の列挙が定数時間であるため、定数時間で動作します。

実装例(C++):

#include<bits/stdc++.h>

using namespace std;

set<long long> gen_tosasu(){
  set<long long> res;
  for(int fir=1;fir<=9;fir++){
    for(int d=-9;d<=8;d++){
      string s;
      int dg=fir;
      for(int i=0;i<18;i++){
        s.push_back(dg+'0');
        res.insert(stoll(s));
        dg+=d;
        if(!(0<=dg && dg<=9)){break;}
      }
    }
  }
  return res;
}

int main(){
  long long x;
  cin >> x;
  set<long long> st=gen_tosasu();
  cout << (*st.lower_bound(x)) << '\n';
  return 0;
}

posted:
last update: