UVa : 11876 - N + NOD (N)

問題概要

http://uva.onlinejudge.org/external/118/11876.html

  • N_0 = 1
  • N_i = N_(i-1) + NOD(N_(i-1))

となるような数列があります。
NOD(N)は、Nの約数の個数を返す関数です。
整数A, Bが入力されたとき、A以上B以下の範囲に、この数列の項がいくつあるかを答えなさい。

制約条件

  • テストケース数 < 100000 (明らかに''やばい''です)
  • 1 <= A <= B <= 1000000

アルゴリズム

まず始めに思いついたのがこれ

A以上B以下の項の数 = B以下の項の数ー(A-1)以下の項の数

これは、O(1)で求められます。
テストケースが10万程度であれば大丈夫ということになります。


つまり、「X以下の項の数を表すような配列」をプログラムの始めで生成できれば勝ちです。
この配列は、素直に数列の1項目から純粋に計算していって、作成することができました。(ちょっとタイム心配してたのですが)
NOD(N)は、O(√N)になるように実装しましょう。

プログラム

int cnt[1000002]; //X以下の項の数を表す配列

//xの約数の数を返す関数
int NOD(int x){
  int res = 0;
  for(int i=1;i*i<=x;i++){
    if(x % i == 0){
      res++;
      if(x / i != i) res++;
    }
  }
  return res;
}

int main(){
  //cnt配列生成処理
  int before = 1;
  cnt[1] = 1;
  while(1){
    int now = before + NOD(before);
    if(now > 1000000){
      REP(i,before+1,1000001) cnt[i] = cnt[i-1];
      break;
    }
    REP(i,before+1,now+1) cnt[i] = cnt[i-1];
    cnt[now]++;
    before = now;
  }

  //テストケース処理
  int T; cin>>T;
  REP(SET,1,T+1){
    int a,b;
    cin>>a>>b;
    cout<<"Case "<<SET<<": "<<cnt[b]-cnt[a-1]<<endl;
  }
}