Ones and Twos
Difficulty: Hard



You are using at most **A** number of 1s and at most **B** number of 2s. How many different evaluation results are possible when they are formed in an expression containing only addition `+` sign and multiplication `*` sign are allowed?

Note that, multiplication takes precedence over addition.

For example, if **A=2** and **B=2**, then we have the following expressions:

*  `1`, `1*1` = 1
*  `2`, `1*2`, `1*1*2`, `1+1` = 2
*  `1+2`, `1+1*2` = 3
*  `2+2`, `2*2`, `1+1+2`, `1*2*2`, `1*1*2*2`, `1*2+1*2`, `1*1*2+2`, `1*2+2` = 4
*  `1+2+2`, `1+1*2+2` = 5
*  `1+1+2+2`,  `1+1+2*2` = 6

So there are 6 unique results that can be formed if A = 2 and B = 2. 


Input Format:
The first line contains the number of test cases T, T testcases follow each in a newline.  
Each testcase contains 2 integers A and B separated by a single space.  


Output Format:
Print the number of different evaluations modulo (%) (10<sup>9</sup>+7.)


Sample Input:
    4
    0 0
    2 2
    0 2
    2 0


Sample Output:
    0
    6
    2
    2


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

def onesAndTwos(a, b):
    # Write your code here



Editorial: Ones and Twos - 2020 Hack March 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><a href="https://www.hackerrank.com/contests/mar14/challenges/ones-and-twos">Ones and Twos</a> <br>
<strong>Difficulty Level</strong> : Medium-Hard <br>
<strong>Required Knowledge</strong> :   DP <br>
<strong>Problem Setter</strong> : <a href="https://www.hackerrank.com/tmt514">Shang En Huang</a> <br>
<strong>Problem Tester</strong> : <a href="https://www.hackerrank.com/dsoboliev">Dmytro Soboliev</a>  </p>

<p><strong>Solution</strong>  </p>

<p>If you are still getting wrong answer but don't know why, please try this test case:</p>

<pre><code> 2
 6 14
 2 1
</code></pre>

<p>The answer should be </p>

<pre><code> 301
 4
</code></pre>

<h3>Part I.</h3>

<p>Let's consider simple cases, say when <strong>A</strong> = 0. In this case, all numbers will be generated by using all 2's. For each integer <strong>X</strong>, consider its binary representation (a<sub>0</sub> + a<sub>1</sub>*2 + a<sub>2</sub>*4+a<sub>3</sub>*8+...). It's not hard to prove you need at least (a<sub>1</sub>*1 + a<sub>2</sub>*2+a<sub>3</sub>*3+...) number of 2's to produce this number.</p>

<p>Hence when given the value of <strong>B</strong> the answer should be:</p>

<blockquote>
  <p>The number of ways partitioning <strong>i &lt;= B</strong> into distinct sum of positive integers.</p>
</blockquote>

<p>This can be solved by <strong>O(B<sup>2</sup>)</strong> dynamic programming: Just let <code>dp[i][j]</code> be the number of ways partitioning <code>i</code> into distinct sum of positive integers with last term (highest bit) equal or less than <code>j</code>. So if we consider the state <code>dp[i][j]</code>, into two situations: the last term is <code>j</code> or not. If yes, there are exactly <code>dp[i-j][j-1]</code> ways. Otherwise it would be <code>dp[i][j-1]</code>. Please consider the following code:</p>

<p><strong>Language : C++</strong></p>

<pre><code>for (int j = 0; j &lt;= B; j++) dp[0][j] = 1;
for (int i = 1; i &lt;= B; i++)
    for (int j = 1; j &lt;= B; j++)
        dp[i][j] = (i&gt;j? dp[i-j][j-1] : 0) + dp[i][j-1];
</code></pre>

<p>Then in the case <strong>A=0</strong>, the solution can be calculated from <code>dp[1][B] + dp[2][B] + ... + dp[B][B]</code>. (Don't forget to mod.)</p>

<h3>Part II: O(T A lg A + B<sup>2</sup>)</h3>

<p>From now on, when we consider an integer, we only consider the representation with fewest 2's.</p>

<p>What if we have some 1's? If using only twos can make a number <strong>X</strong>, then we can build all integers in the ranage <strong>[X, X+A]</strong>. Suppose we have a number <strong>2<sup>K</sup></strong>, where <strong>2A &lt; 2<sup>K</sup></strong>. We split the terms by "Terms less than*<em>2<sup>K</sup></em>* and terms no less than <strong>2<sup>K</sup></strong>". For terms less than <strong>2<sup>K</sup></strong>. We will know that the sum of all parts contributed by 2's will be less than <strong>2<sup>K</sup></strong> (Why?).</p>

<p>Hence, if we choose minimal <strong>K</strong> with <strong>2A &lt; 2<sup>K</sup></strong>, then we can guarantee that the sum of whole part less than <strong>2<sup>K</sup></strong> will still less than <strong>2<sup>K</sup></strong>. (Given the condition that we use least number of 2's.)</p>

<p></p>

<p>For each <strong>t, 0 &lt;= t &lt; 2<sup>K</sup></strong>, let <strong>R[t]</strong> be the least number of 2's which can build <strong>t</strong>. Modify the DP table from part I: <code>dp[i][j]</code> be the number of ways partitioning <code>i</code> into distinct sum of positive integers with last term (highest bit) equal or less than <code>j</code> <em>and with first term (lowest bit) at least <b>K</b></em>.</p>

<pre><code>cpp
for (int j = 0; j &lt;= B; j++) dp[0][j] = 1;
for (int i = 1; i &lt;= B; i++)
    for (int j = 1; j &lt;= B; j++)
        dp[i][j] = (j&gt;=K &amp;&amp; i&gt;j ? dp[i-j][j-1] : 0) + dp[i][j-1];
</code></pre>

<p>In this <strong>t</strong>, the ways to make numbers that modulo <strong>2<sup>K</sup></strong> equals <strong>t</strong> is exactly <code>dp[1][B] + dp[2][B] + ... + dp[B - R[t]][B]</code>. (Please mod.)</p>

<p>We can easily get the relations <code>R[0] = R[1] = ... = R[A] = 0</code>, and for <strong>t &gt; A</strong>,
<strong>R[t]</strong> = min<sub><strong>0 &lt;= j &lt; K</strong></sub>{ <strong>R[t-2<sup>j</sup>] + j</strong> }</p>

<p>In conclusion, for each test case, we can go over each <strong>0 &lt;= t &lt; 2<sup>K</sup></strong>. Then the time complexity is <strong>O(T K 2<sup>K</sup> + B^2) = O(T A lg A + B^2)</strong>.</p>

<h3>Part III: O(T lg A + B^2)</h3>

<p>The remaining parts:</p>

<ol>
<li>calculate <strong>R[0]..R[2<sup>K</sup>-1]</strong> efficiently</li>
<li>calculate the sum <strong>dp[ B-R[i] ][ B-R[i] ]</strong> efficiently</li>
</ol>

<p>In order to solve (2), we need to count "How many <strong>i</strong>'s are there such that <strong>R[i] = r</strong>?" for each <strong>r = 0, 1, ..., 2<sup>K</sup>-1</strong>.</p>

<p>(Maybe not) Surprisingly, we have the following properties:</p>

<p></p>

<blockquote>
  <p><strong>Prop</strong>: for any integer no more than <strong>2<sup>K-1</sup></strong>, we only need at most one "2 term".</p>
  
  <p><strong>Prop2</strong>: for any integer in <strong>(2<sup>K-1</sup>, 2<sup>K</sup>)</strong> we use at most two "2 terms".</p>
</blockquote>

<p>Hence we can find the sum with a single loop enumerating from 0 to K-1.</p>

<p>Happy thinking &amp; happy coding :)</p>

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

<pre><code>#include &lt;cstdio&gt;
#include &lt;cstring&gt;
#include &lt;cmath&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

#define FOR(it, c) for(__typeof((c).begin()) it = (c).begin(); it != (c).end(); it++)
#define SZ(c) ((int)(c).size())

typedef long long LL;
int MOD = 1e9+7;

int p[33][1005], up[33]={};
void add(int &amp;x, LL v) {
    x = (x+v)%MOD;
}

const int MXB = 1000;
void pre(int S) {
    if(up[S]) return;
    up[S] = 1;
    p[S][0] = 1;
    for(int l=S;l&lt;=MXB;l++) {
        for(int j=MXB;j&gt;=l;j--)
            add(p[S][j], p[S][j-l]);
    }
    for(int j=1;j&lt;=MXB;j++) add(p[S][j], p[S][j-1]);
    //printf("build: "); for(int j=0;j&lt;=MXB;j++) printf("%d ", p[S][j]); puts("");
}

void solve() {
    int A, B, s;
    LL t;
    scanf("%d%d", &amp;A, &amp;B);

    if (A == 0) {
        pre(1);
        printf("%d\n", (p[1][B]+MOD-1)%MOD);
        return;
    }

    for(t=2,s=1;t&lt;=A;t*=2,s++);
    t *= 2; s++;
    pre(s);

    //printf("s=%d\n", s);

    int ans = 0;
    LL last = 0, last2 = t/2+A+1;
    for(int u=0;u&lt;s &amp;&amp; u&lt;=B;u++) {
        LL nxt = min(t, (1LL&lt;&lt;u) + A + (u&gt;0));
        LL nxt2 = min(t, (1LL&lt;&lt;u) + A + t/2 + (u&gt;0));
        add(ans, (nxt-last)*1LL*p[s][B-u]);
        if(u+s-1&lt;=B) add(ans, (nxt2-last2)*1LL*p[s][B-(u+s-1)]);
        //printf("u=%d: [%I64d, %I64d)  %d; [%I64d, %I64d)  \n", u, last, nxt, p[s][B-u], last2, nxt2);
        last = nxt;
        last2 = nxt2;
    }
    //printf("nxt=%I64d\n", last);
    printf("%d\n", (ans+MOD-1)%MOD);
}

int main(void) {
    int T;
    scanf("%d", &amp;T);
    while(T--) solve();
    return 0;
}
</code></pre>

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

<pre><code>#include &lt;iostream&gt;
#include &lt;cstdio&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;ctime&gt;
#include &lt;set&gt;
#include &lt;cassert&gt;

using namespace std;

const int P = 1000000007;
const int maxN = 1100;
int d[maxN][maxN], s[maxN][maxN];

void precalc() {
    d[maxN - 1][0] = 1;
    for (int i = maxN - 1; i &gt; 0; --i) {
        for (int j = 0; j &lt; maxN; ++j) {
            if (d[i][j] == 0) {
                continue;
            }

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

            if (j + (i - 1) &lt; maxN) {
                d[i - 1][j + (i - 1)] += d[i][j];
                if (d[i - 1][j + (i - 1)] &gt;= P) {
                    d[i - 1][j + (i - 1)] -= P;
                }
            }
        }
    }

    for (int i = 0; i &lt; maxN; ++i) {
        for (int j = 0; j &lt; maxN; ++j) {
            s[i][j] = d[i][j];
            if (j &gt; 0) {
                s[i][j] += s[i][j - 1];
                if (s[i][j] &gt;= P) {
                    s[i][j] -= P;
                }
            }
        }
    }
}

void rec(int left, int position, int current, vector &lt; long long &gt;* numbers) {
    if (position == 0) {
        numbers-&gt;push_back(current);
        return ;
    }
    rec(left, position - 1, current, numbers);
    if (left &gt;= position) {
        rec(left - position, position - 1, current + (1 &lt;&lt; position), numbers);
    }
}

long long getWays(int a, int n, int up, long long&amp; no_next) {
    vector &lt; long long &gt; numbers;
    rec(n, up - 1, 0, &amp;numbers);
    sort(numbers.begin(), numbers.end());

    long long res = 1;
    long long last = 0;
    for (int i = 0; i &lt; numbers.size(); ++i) {
        if (numbers[i] &gt; last + a) {
            res += a;
            last = numbers[i];
            ++res;
        } else {
            res += numbers[i] - last;
            last = numbers[i];
        }
    }

    no_next = (res + a) % P;

    long long bound = (1 &lt;&lt; up) - 1;
    if (bound &gt; last + a) {
        res += a;
    } else {
        res += bound - last;
    }
    res %= P;
    return res;
}

const int maxM = 40;
vector &lt; int &gt; bounds[maxM][maxM];
vector &lt; int &gt; scores[maxM][maxM];
vector &lt; int &gt; add[maxM][maxM];
int up_bound[maxM][maxM];

void prebuild() {
    for (int up = 1; up &lt;= 30; ++up) {
        for (int n = 1; n &lt;= up; ++n) {
            vector &lt; long long &gt; numbers;
            rec(n, up - 1, 0, &amp;numbers);
            sort(numbers.begin(), numbers.end());

            set &lt; long long &gt; diff;
            for (int i = 1; i &lt; numbers.size(); ++i) {
                if (numbers[i] != numbers[i - 1] + 1) {
                    diff.insert(numbers[i] - numbers[i - 1] - 1);
                }
            }
            if ((1 &lt;&lt; up) - 1 != numbers.back() + 1) {
                diff.insert((1 &lt;&lt; up) - numbers.back() - 2);
            }
            for (set &lt; long long &gt; :: iterator it = diff.begin(); it != diff.end(); ++it) {
                bounds[up][n].push_back(*it);
                scores[up][n].push_back(0);
                add[up][n].push_back(0);
            }

            for (int index = 0; index &lt; bounds[up][n].size(); ++index) {
                long long value = bounds[up][n][index];
                long long last = 0;
                scores[up][n][index] = 1;
                for (int i = 0; i &lt; numbers.size(); ++i) {
                    if (numbers[i] &gt; last + value) {
                        ++add[up][n][index];
                        ++scores[up][n][index];
                        last = numbers[i];
                    } else {
                        scores[up][n][index] += numbers[i] - last;
                        last = numbers[i];
                    }
                }
                ++add[up][n][index];
            }
            up_bound[up][n] = numbers.back();
        }
    }
}

long long getWaysFast(int a, int n, int up, long long&amp; no_next) {
    int index = lower_bound(bounds[up][n].begin(), bounds[up][n].end(), a) - bounds[up][n].begin();
    if (index == bounds[up][n].size()) {
        no_next = (up_bound[up][n] + a + 1) % P;
        return min((1 &lt;&lt; up) - 1, up_bound[up][n] + a) + 1;
    }

    long long score = scores[up][n][index];
    score += (long long)(a) * (long long)(add[up][n][index]);
    score %= P;

    no_next = score;
    if (up_bound[up][n] + a &gt; (1 &lt;&lt; up) - 1) {
        score -= up_bound[up][n] + a - ((1 &lt;&lt; up) - 1);
    }
    return score;
}

void solve() {
    int a, b;
    scanf("%d%d", &amp;a, &amp;b);
    assert(0 &lt;= a &amp;&amp; a &lt;= 1e9);
    assert(0 &lt;= b &amp;&amp; b &lt;= 1e3);
    if (a == 0) {
        printf("%d\n", (s[1][b] + P - 1) % P);
        return ;
    }

    int up = 0;
    for (int i = 1; ; ++i) {
        if ((1 &lt;&lt; i) &gt; a) {
            up = i;
            break;
        }
    }

    long long res = 0;
    if (b &gt;= up + 5) {
        res += (long long)(s[up][b - up - 4]) * (long long)(1 &lt;&lt; up);
        res %= P;
    }
    for (int i = (b &gt;= up + 5 ? (b - up - 3) : 0); i &lt; b; ++i) {
        int current = d[up][i];
        if (current == 0) {
            continue;
        }
        int n = b - i;

        long long up_on = d[up][i] - d[up + 1][i];
        if (up_on &lt; 0) {
            up_on += P;
        }
        long long up_off = d[up + 1][i];

        long long has_next = up_on;
        long long no_next = up_off;

        if (i + up &lt;= b) {
            has_next = (up_on + up_off) % P;
            no_next = 0;
        }

        if (n &gt; up) {
            res += has_next * (long long)(1 &lt;&lt; up);
            res %= P;
            res += no_next * (long long)((1 &lt;&lt; up) + a);
            res %= P;
        } else {
            long long q;
            res += has_next * getWaysFast(a, n, up, q);
            res %= P;
            res += no_next * q;
            res %= P;
        }
    }

    {
        res += (long long)(d[up][b]) * (long long)(a + 1);
        res %= P;
    }

    res = (res + P - 1) % P;

    printf("%d\n", (int)(res));
}

int main() {
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);

    precalc();
    prebuild();

    int tests;
    scanf("%d", &amp;tests);
    assert(1 &lt;= tests &amp;&amp; tests &lt;= 1e5);
    for (int i = 0; i &lt; tests; ++i) {
        solve();
    }
    return 0;
}
</code></pre>

Reference solution (C++):
###C++  
```cpp  
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <ctime>
#include <set>
#include <cassert>
    
using namespace std;
    
const int P = 1000000007;
const int maxN = 1100;
int d[maxN][maxN], s[maxN][maxN];
    
void precalc() {
    d[maxN - 1][0] = 1;
    for (int i = maxN - 1; i > 0; --i) {
        for (int j = 0; j < maxN; ++j) {
            if (d[i][j] == 0) {
                continue;
            }
    
            d[i - 1][j] += d[i][j];
            if (d[i - 1][j] >= P) {
                d[i - 1][j] -= P;
            }
    
            if (j + (i - 1) < maxN) {
                d[i - 1][j + (i - 1)] += d[i][j];
                if (d[i - 1][j + (i - 1)] >= P) {
                    d[i - 1][j + (i - 1)] -= P;
                }
            }
        }
    }
    
    for (int i = 0; i < maxN; ++i) {
        for (int j = 0; j < maxN; ++j) {
            s[i][j] = d[i][j];
            if (j > 0) {
                s[i][j] += s[i][j - 1];
                if (s[i][j] >= P) {
                    s[i][j] -= P;
                }
            }
        }
    }
}
    
void rec(int left, int position, int current, vector < long long >* numbers) {
    if (position == 0) {
        numbers->push_back(current);
        return ;
    }
    rec(left, position - 1, current, numbers);
    if (left >= position) {
        rec(left - position, position - 1, current + (1 << position), numbers);
    }
}
    
long long getWays(int a, int n, int up, long long& no_next) {
    vector < long long > numbers;
    rec(n, up - 1, 0, &numbers);
    sort(numbers.begin(), numbers.end());
    
    long long res = 1;
    long long last = 0;
    for (int i = 0; i < numbers.size(); ++i) {
        if (numbers[i] > last + a) {
            res += a;
            last = numbers[i];
            ++res;
        } else {
            res += numbers[i] - last;
            last = numbers[i];
        }
    }
    
    no_next = (res + a) % P;
    
    long long bound = (1 << up) - 1;
    if (bound > last + a) {
        res += a;
    } else {
        res += bound - last;
    }
    res %= P;
    return res;
}
    
const int maxM = 40;
vector < int > bounds[maxM][maxM];
vector < int > scores[maxM][maxM];
vector < int > add[maxM][maxM];
int up_bound[maxM][maxM];
    
void prebuild() {
    for (int up = 1; up <= 30; ++up) {
        for (int n = 1; n <= up; ++n) {
            vector < long long > numbers;
            rec(n, up - 1, 0, &numbers);
            sort(numbers.begin(), numbers.end());
    
            set < long long > diff;
            for (int i = 1; i < numbers.size(); ++i) {
                if (numbers[i] != numbers[i - 1] + 1) {
                    diff.insert(numbers[i] - numbers[i - 1] - 1);
                }
            }
            if ((1 << up) - 1 != numbers.back() + 1) {
                diff.insert((1 << up) - numbers.back() - 2);
            }
            for (set < long long > :: iterator it = diff.begin(); it != diff.end(); ++it) {
                bounds[up][n].push_back(*it);
                scores[up][n].push_back(0);
                add[up][n].push_back(0);
            }
    
            for (int index = 0; index < bounds[up][n].size(); ++index) {
                long long value = bounds[up][n][index];
                long long last = 0;
                scores[up][n][index] = 1;
                for (int i = 0; i < numbers.size(); ++i) {
                    if (numbers[i] > last + value) {
                        ++add[up][n][index];
                        ++scores[up][n][index];
                        last = numbers[i];
                    } else {
                        scores[up][n][index] += numbers[i] - last;
                        last = numbers[i];
                    }
                }
                ++add[up][n][index];
            }
            up_bound[up][n] = numbers.back();
        }
    }
}
    
long long getWaysFast(int a, int n, int up, long long& no_next) {
    int index = lower_bound(bounds[up][n].begin(), bounds[up][n].end(), a) - bounds[up][n].begin();
    if (index == bounds[up][n].size()) {
        no_next = (up_bound[up][n] + a + 1) % P;
        return min((1 << up) - 1, up_bound[up][n] + a) + 1;
    }
    
    long long score = scores[up][n][index];
    score += (long long)(a) * (long long)(add[up][n][index]);
    score %= P;
    
    no_next = score;
    if (up_bound[up][n] + a > (1 << up) - 1) {
        score -= up_bound[up][n] + a - ((1 << up) - 1);
    }
    return score;
}
    
void solve() {
    int a, b;
    scanf("%d%d", &a, &b);
    assert(0 <= a && a <= 1e9);
    assert(0 <= b && b <= 1e3);
    if (a == 0) {
        printf("%d\n", (s[1][b] + P - 1) % P);
        return ;
    }
    
    int up = 0;
    for (int i = 1; ; ++i) {
        if ((1 << i) > a) {
            up = i;
            break;
        }
    }
    
    long long res = 0;
    if (b >= up + 5) {
        res += (long long)(s[up][b - up - 4]) * (long long)(1 << up);
        res %= P;
    }
    for (int i = (b >= up + 5 ? (b - up - 3) : 0); i < b; ++i) {
        int current = d[up][i];
        if (current == 0) {
            continue;
        }
        int n = b - i;
    
        long long up_on = d[up][i] - d[up + 1][i];
        if (up_on < 0) {
            up_on += P;
        }
        long long up_off = d[up + 1][i];
    
        long long has_next = up_on;
        long long no_next = up_off;
    
        if (i + up <= b) {
            has_next = (up_on + up_off) % P;
            no_next = 0;
        }
    
        if (n > up) {
            res += has_next * (long long)(1 << up);
            res %= P;
            res += no_next * (long long)((1 << up) + a);
            res %= P;
        } else {
            long long q;
            res += has_next * getWaysFast(a, n, up, q);
            res %= P;
            res += no_next * q;
            res %= P;
        }
    }
    
    {
        res += (long long)(d[up][b]) * (long long)(a + 1);
        res %= P;
    }
    
    res = (res + P - 1) % P;
    
    printf("%d\n", (int)(res));
}
    
int main() {
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
    
    precalc();
    prebuild();
    
    int tests;
    scanf("%d", &tests);
    assert(1 <= tests && tests <= 1e5);
    for (int i = 0; i < tests; ++i) {
        solve();
    }
    return 0;
}
    
```