P-sequences
Difficulty: Hard

We call a sequence of `N` natural numbers (*a*<sub>1</sub>, *a*<sub>2</sub>, ..., *a*<sub>N</sub>) a *P-sequence*, if the product of any two adjacent numbers in it is not greater than *P*. In other words, if a sequence (*a*<sub>1</sub>, *a*<sub>2</sub>, ..., *a*<sub>N</sub>) is a *P-sequence*, then *a*<sub>i</sub> * *a*<sub>i+1</sub> &le; `P` &forall; 1 &le; i &lt; N

You are given `N` and `P`. Your task is to find the number of such *P-sequences* of `N` integers modulo 10<sup>9</sup>+7.


Input Format:
The first line of input consists of `N`  
The second line of the input consists of `P`. 


Output Format:
Output the number of *P-sequences* of `N` integers modulo 10<sup>9</sup>+7.


Sample Input:
    2
    2


Sample Output:
    3


Python 3 starter code:
#
# Complete the 'pSequences' function below.
#
# The function is expected to return an INTEGER.
# The function accepts following parameters:
#  1. INTEGER n
#  2. INTEGER p
#

def pSequences(n, p):
    # Write your code here



Editorial: P-Sequences - Week 1 Editorial
<style id="MathJax_SVG_styles">.MathJax_SVG_Display {text-align: center; margin: 1em 0em; position: relative; display: block!important; text-indent: 0; max-width: none; max-height: none; min-width: 0; min-height: 0; width: 100%}
.MathJax_SVG .MJX-monospace {font-family: monospace}
.MathJax_SVG .MJX-sans-serif {font-family: sans-serif}
.MathJax_SVG {display: inline; font-style: normal; font-weight: normal; line-height: normal; font-size: 100%; font-size-adjust: none; text-indent: 0; text-align: left; text-transform: none; letter-spacing: normal; word-spacing: normal; word-wrap: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0; min-height: 0; border: 0; padding: 0; margin: 0}
.MathJax_SVG * {transition: none; -webkit-transition: none; -moz-transition: none; -ms-transition: none; -o-transition: none}
.mjx-svg-href {fill: blue; stroke: blue}
</style><svg style="display: none;"><defs id="MathJax_SVG_glyphs"></defs></svg><p><strong>Difficulty level</strong> : Medium <br>
<strong>Required knowledge</strong> : Partial sums trick, DP <br>
<strong>Problem setter</strong> : <a href="https://www.hackerrank.com/zxqfd555">Sergey Kulik</a> <br>
<strong>Problem tester</strong> : <a href="https://www.hackerrank.com/dsoboliev">Dmytro Soboliev</a>  </p>

<p><strong>Approach</strong></p>

<p>Consider a valid P-sequence of <strong>N</strong> integers: (<strong>a<sub>1</sub></strong>, <strong>a<sub>2</sub></strong>, ..., <strong>a<sub>N</sub></strong>). If you take some <strong>i</strong> (1 &lt;= <strong>i</strong> &lt; <strong>N</strong>) and take a pair (<strong>a<sub>i</sub></strong>, <strong>a<sub>i+1</sub></strong>) from this sequence, at least one of the following statements will be true:</p>

<ul>
<li><strong>a<sub>i</sub></strong> is not greater than the square root (in future, to be short, sqrt) of <strong>P</strong>.</li>
<li><strong>a<sub>i+1</sub></strong> is not greater than the square root of <strong>P</strong>.</li>
</ul>

<p>And this fact gives a rise to the key idea of the solution. Let's have a DP <strong>F(N, R, t)</strong>. The meaning of the DP state is: the number of P-sequences of <strong>N</strong> integer numbers, where:</p>

<ul>
<li>if <strong>t</strong> is false, then <strong>R</strong> is the <strong>N</strong>-th term of a P-sequence.</li>
<li>if <strong>t</strong> is true, then <strong>R</strong> is the maximal possible number that can be the <strong>N+1</strong>-th term of a P-sequence.</li>
</ul>

<p>Basically, false value of <strong>t</strong> corresponds to the above case where <strong>a<sub>i</sub></strong> isn't greater than <strong>sqrt(P)</strong> and true value of <strong>t</strong> corresponds to the case where <strong>a<sub>i</sub></strong> is actually greater than <strong>sqrt(P)</strong>, but <strong>a<sub>i+1</sub></strong> is not greater than <strong>sqrt(P)</strong>.</p>

<p>We have O(<strong>N</strong> * sqrt(<strong>P</strong>)) states here, so we can calculate this DP in affordable time.</p>

<p>When we calculate the DP, we should take care of the initial DP values and about the DP transitions.</p>

<p>The initial DP values are the values of <strong>F(N, R, t)</strong> where <strong>N</strong> is equal to one. Here </p>

<ul>
<li><p><strong>F(1, R, false)</strong> equals to one because <strong>R</strong> is the the only term of a P-sequence sequence here.</p></li>
<li><p><strong>F(1, R, true)</strong> equals to the number of such integers <strong>K</strong> from the range [1; <strong>P</strong>] such that <strong>N</strong> divided by <strong>K</strong> equals to <strong>R</strong>. This number can be obtained by solving the corresponding equation, see the solution for the details.</p></li>
</ul>

<p>When <strong>t</strong> (that means that <strong>a<sub>i+1</sub></strong> is not greater than <strong>sqrt(P)</strong>) is false you should take the sum of the following values in order to get <strong>F(N, R, t)</strong>:</p>

<ul>
<li><p>the sum of <strong>F(N - 1, R', false)</strong>, where <strong>R'</strong> for <strong>R'</strong> from <strong>1</strong> to <strong>sqrt(P)</strong>. This corresponds to the case where the both <strong>a<sub>i</sub></strong> and <strong>a<sub>i+1</sub></strong> are not greater than <strong>sqrt(P)</strong>.</p></li>
<li><p>the sum of <strong>F(N - 1, R', true)</strong>, where <strong>R'</strong> is greater or equal to <strong>R</strong>. This follows derives from the above DP state definition and corresponds to the case when <strong>a<sub>i</sub></strong> is greater than <strong>sqrt(P)</strong> but <strong>a<sub>i+1</sub></strong> is smaller than <strong>sqrt(P)</strong>.</p></li>
</ul>

<p>When we have the true value of <strong>t</strong>, the transition is simpler. Here the value of <strong>F(N, R, t)</strong> equals to the sum of <strong>F(N - 1, R', t)</strong>, where <strong>R'</strong> is not greater than <strong>R</strong> and then multiplied by the amount of such numbers <strong>K</strong> in the range [1; <strong>P</strong>] that <strong>P</strong> divided by <strong>K</strong> is <strong>R</strong>.</p>

<p>Here we're taking a sum of a multiple previously calculated DP states, but using, for example, the partial sums trick, we can do it in O(1).</p>

<p><strong>Setter's solution</strong></p>

<pre><code>#include &lt;iostream&gt;
#include &lt;cstdio&gt;
#include &lt;algorithm&gt;
using namespace std;

#define maxn 1000 + 5
#define max_sqrt_k 32000
#define mod 1000000007

int dp[2][max_sqrt_k][2], n, k, sqrt_k, i, j, t, ret, pref, sum0, sum1, count_lr[max_sqrt_k], le, ri, l, p;

void upd (int &amp;a, int b) {
    a += b;
    if (a &gt;= mod) a -= mod;
}

int main (int argc, char * const argv[]) {
    scanf("%d %d", &amp;n, &amp;k);
    while (sqrt_k * sqrt_k &lt; k) ++sqrt_k;
    for(i = 1; i &lt;= sqrt_k; i++) dp[1][i][0] = 1;
    for(i = 1; i &lt; sqrt_k; i++) {
        le = k / (i + 1) + 1;
        ri = k / i;
        if (ri &lt;= sqrt_k) continue;
        le = max(le, sqrt_k + 1);
        count_lr[i] = ri - le + 1;
    }
    for(i = 1; i &lt; sqrt_k; i++) dp[1][i][1] = count_lr[i];
    l = 1;
    for(i = 2; i &lt;= n; i++) {
        p = l;
        l ^= 1;
        sum0 = sum1 = 0;
        for(j = 1; j &lt;= sqrt_k; j++) {
            upd(sum0, dp[p][j][0]);
            upd(sum1, dp[p][j][1]);
        }
        t = sqrt_k;
        for(j = 1; j &lt;= sqrt_k; j++) {
            while (j * t &gt; k) upd(sum0, mod - dp[p][t--][0]);
            dp[l][j][0] = sum1 + sum0;
            if (dp[l][j][0] &gt;= mod) dp[l][j][0] -= mod;
            upd(sum1, mod - dp[p][j][1]);
        }
        pref = 0;
        for(j = 1; j &lt; sqrt_k; j++) {
            upd(pref, dp[p][j][0]);
            dp[l][j][1] = (count_lr[j] * 1LL * pref) % mod;
        }
    }
    for(j = 1; j &lt;= sqrt_k; j++) {
        upd(ret, dp[l][j][0]);
        upd(ret, dp[l][j][1]);
    }
    printf("%d\n", ret);
    return 0;
}
</code></pre>

<p><strong>Tester's Solution</strong></p>

<pre><code>/*
    Solution by sdya (Dmytro Soboliev)
*/
#include &lt;iostream&gt;
#include &lt;cstdio&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;cstring&gt;
#include &lt;cassert&gt;

using namespace std;

const int P = 1000000007;
const int maxN = 64000;

vector &lt; int &gt; bounds, ways;
int d[2][maxN], add[maxN];

int buildBounds(int p) {
  for (int i = 1; i * i &lt;= p; ++i) {
    ways.push_back(i);
    bounds.push_back(p / i);
  }

  int m = bounds.size();
  for (int i = 0; i &lt; m; ++i) {
    ways.push_back(bounds[m - 1 - i]);
    bounds.push_back(ways[m - 1 - i]);
  }

  return m + m;
}

int main() {
  int n, p;
  cin &gt;&gt; n &gt;&gt; p;
  assert(1 &lt;= n &amp;&amp; n &lt;= 1e3);
  assert(1 &lt;= p &amp;&amp; p &lt;= 1e9);
  int m = buildBounds(p);

  int u = 0, v = 1;
  memset(d[u], 0, sizeof(d[u]));
  d[u][m - 1] = 1;
  for (int i = 0; i &lt; n; ++i, swap(u, v)) {
    int covered = m - 1;
    memset(add, 0, sizeof(add));
    memset(d[v], 0, sizeof(d[v]));
    for (int j = 0; j &lt; m; ++j) {
      if (d[u][j] == 0) {
        continue;
      }
      while (covered &gt;= 0 &amp;&amp; bounds[covered] &lt;= ways[j]) {
        --covered;
      }

      add[covered + 1] += d[u][j];
      if (add[covered + 1] &gt;= P) {
        add[covered + 1] -= P;
      }

      for (int k = covered; k &gt;= 0; --k) {
        int right = min(bounds[k], ways[j]);
        int left = k == m - 1 ? 1 : bounds[k + 1] + 1;

        if (left &lt;= right) {
          d[v][k] += (long long)(d[u][j]) * (long long)(right - left + 1) % (long long)(P);
          if (d[v][k] &gt;= P) {
            d[v][k] -= P;
          }
        } else {
          break;
        }
      }
    }

    for (int j = 1; j &lt; m; ++j) {
      add[j] += add[j - 1];
      if (add[j] &gt;= P) {
        add[j] -= P;
      }
    }

    for (int j = 0; j &lt; m; ++j) {
      int right = bounds[j];
      int left = j == m - 1 ? 1 : bounds[j + 1] + 1;
      d[v][j] += (long long)(add[j]) * (long long)(right - left + 1) % (long long)(P);
      if (d[v][j] &gt;= P) {
        d[v][j] -= P;
      }
    }
  }

  long long res = 0;
  for (int i = 0; i &lt; m; ++i) {
    res += d[u][i];
  }

  cout &lt;&lt; res % P &lt;&lt; endl;

  return 0;
}
</code></pre>

Reference solution (C++):
###C++  
```cpp  

/*
    Solution by sdya (Dmytro Soboliev)
*/
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cassert>
    
using namespace std;
    
const int P = 1000000007;
const int maxN = 64000;
    
vector < int > bounds, ways;
int d[2][maxN], add[maxN];
    
int buildBounds(int p) {
  for (int i = 1; i * i <= p; ++i) {
    ways.push_back(i);
    bounds.push_back(p / i);
  }
  
  int m = bounds.size();
  for (int i = 0; i < m; ++i) {
    ways.push_back(bounds[m - 1 - i]);
    bounds.push_back(ways[m - 1 - i]);
  }
  
  return m + m;
}
    
int main() {
  int n, p;
  cin >> n >> p;
  assert(1 <= n && n <= 1e3);
  assert(1 <= p && p <= 1e9);
  int m = buildBounds(p);
  
  int u = 0, v = 1;
  memset(d[u], 0, sizeof(d[u]));
  d[u][m - 1] = 1;
  for (int i = 0; i < n; ++i, swap(u, v)) {
    int covered = m - 1;
    memset(add, 0, sizeof(add));
    memset(d[v], 0, sizeof(d[v]));
    for (int j = 0; j < m; ++j) {
      if (d[u][j] == 0) {
        continue;
      }
      while (covered >= 0 && bounds[covered] <= ways[j]) {
        --covered;
      }
      
      add[covered + 1] += d[u][j];
      if (add[covered + 1] >= P) {
        add[covered + 1] -= P;
      }
    
      for (int k = covered; k >= 0; --k) {
        int right = min(bounds[k], ways[j]);
        int left = k == m - 1 ? 1 : bounds[k + 1] + 1;
    
        if (left <= right) {
          d[v][k] += (long long)(d[u][j]) * (long long)(right - left + 1) % (long long)(P);
          if (d[v][k] >= P) {
            d[v][k] -= P;
          }
        } else {
          break;
        }
      }
    }
    
    for (int j = 1; j < m; ++j) {
      add[j] += add[j - 1];
      if (add[j] >= P) {
        add[j] -= P;
      }
    }
    
    for (int j = 0; j < m; ++j) {
      int right = bounds[j];
      int left = j == m - 1 ? 1 : bounds[j + 1] + 1;
      d[v][j] += (long long)(add[j]) * (long long)(right - left + 1) % (long long)(P);
      if (d[v][j] >= P) {
        d[v][j] -= P;
      }
    }
  }
    
  long long res = 0;
  for (int i = 0; i < m; ++i) {
    res += d[u][i];
  }
  
  cout << res % P << endl;
  
  return 0;
}
```