InfiniteSequence topcoder SRM413 div2 level3

Introduction

If you have not tried to solve this problem by yourself, please do so. Otherwise, this post will make little sense to you.

The statement of the problem is a recursion, , . are integers. The symbol represents the floor. The question is to find the value of for any given , , and . The constraints are that and .

A Simpler Problem: When p Equals q

Often times, we may find some inspiration by solving a simpler version of the problem. Let us assume . The recursion becomes . Can we solve this one? One thing we notice is that, the consecutive may have the same value if are the same, i.e. . So, instead of computing values, we can only compute values. If , are in the same equivalent class. But this is not good enough. Let’s see how to compute . . This is interesting. Although 12 can be divided by 3, but the intermediate index 4 cannot, which results that . It looks like there is a better way to define the equivalent class. This also means that . This is an Ah-ha moment, the (1D) space is actually divided into equivalent classes by the power of . So, for any given , the is equal to the closest such that , where is some integer. And since the recursion is very simple, we can find immediately: or in code 2ull << (int)floor( log(n) / log(p) );. The time complexity is .

A Progressing Algorithm

When , the space is in 2D. The space is divided into equivalent classes by the combination of powers of and . For example, if and , any number will have the same value as , where , where and are integers and is the largest number no greater than . Let’s denote as the set of all numbers like given bases and . It might be possible to solve this recursion analytically, like the simpler case above, yielding a constant time algorithm. But I have not figured out how to do so. Alternatively, it is feasible to just generate all using a progressing algorithm. (I call this algorithm progressing. Please let me know if you know its real name.) Notice that, is not ; is the representer of the equivalent class containing . The size of , with as the largest element, should be in the scale of .

The algorithm goes as the following. Keep an array of pairs of index and its value . Initialize this array, v, by the base cases: and . We will also keep two running indices pidx and qidx, initialized at 1. Then, we keep generating the next multiplier by picking the smaller one between p*v[pidx].first and q* v[qidx].first, u. Then, calculate its value by looking up the values of [u/p] and [u/q] using binary searches. Add the pair ( u, a_u ) into v, and increment the running index (p, or q, or both), from which we pick u from. We keep doing this until .

Time Complexity

As the maximum number of elements in the vector is , where is the largest element in that is no greater than . At each step, we have to perform two binary search taking , where is the size of the vector at that step. Therefore, the total time complexity is , which is . We can use the Sterling formula to approximate it as . For the largest case, where and and , . So, this algorithm is efficient enough.

class InfiniteSequence {
  public:
    long calc( long n, int p, int q ) {
      if( n == 0 ) return 1;
      if( n == 1 ) return 2;
      if( p == q ){
        return 2ull << (int)floor( log(n) / log(p) );
      }
      if( p > q ) swap( p,q ); // p < q
      vector< pair< long, long > > v; // n and a[n]
      v.push_back( make_pair( 0, 1) );
      v.push_back( make_pair( 1, 2) );
      int pidx = 1;
      int qidx = 1;
      while( v.back().first < n ){
        long t = v[pidx].first * p ;
        long w = v[qidx].first * q ;
        long u = min( t, w );
        long x = u/p;
        long y = u/q;
        auto itr1 = lower_bound( v.begin(), v.end(), make_pair(x, 0l) );
        auto itr2 = lower_bound( v.begin(), v.end(), make_pair(y, 0l) );
        if( itr1->first > x ) --itr1;
        if( itr2->first > y ) --itr2;
        v.push_back( make_pair( u, itr1->second + itr2->second ) );
        if( t == w ){
          ++ pidx;
          ++ qidx;
        }else if( t < w ){ 
          ++ pidx;
        }else{ // t > w
          ++ qidx;
        }
      }
      if( v.back().first == n ){
        return v.back().second;
      }else{
        return (v.end()-2)->second;
      }
    }
};

Written on April 1, 2017