(Analysis by Benjamin Qi, Jesse Choe)

Suppose that we can compute the answers for all prefixes of the input array in $T$ time. Then computing the answer for all contiguous subarrays of the input array can be done in $O(NT)$ time, and answering the queries takes $O(Q)$ additional time.

For all subtasks, we use dynamic programming.

Subtask 1: $T=O(NB)$

For each $i\in [0,N]$ and $x\in [0,B]$, let $dp[i][x]$ denote the number of ways to form $x$ after moving over papers $1\dots i$. The answer for the prefix $1\dots i$ is $\sum_{j=A}^Bdp[i][j]$.

Next, we describe how to compute all of these DP states. We initialize $dp[0][0]=1$ and use the following reasoning to generate $dp[i]$ from $dp[i-1]$. Suppose our pile currently has size $s$, has value $k$ when read from top to bottom, and that the cows are considering adding digit $a_i$ to the pile.

Since each DP transition runs in constant time, computing all these DP states takes time proportional to the number of DP states, which is $O(NB)$.

Jesse Choe's code:

#include <bits/stdc++.h>

using namespace std;

const int MOD = 1e9 + 7;

int countDigits(int x) {
	int ans = 0;
	while (x) { ans++, x /= 10; }
	return ans;
}

int main() {
	int n;
	long long a, b;
	cin >> n >> a >> b;
	int dp[n][n + 1][b + 1]{};
	vector<int> d(n);
	for (int i = 0; i < n; i++) cin >> d[i];
	for (int i = 0; i < n; i++) { dp[i][i][0] = 1; }
	for (int i = 0; i < n; i++) {
		for (int j = i; j < n; j++) {
			for (int k = 0; k <= b; k++) {
				dp[i][j + 1][k] += dp[i][j][k], dp[i][j + 1][k] %= MOD;
				int addRight = 10 * k + d[j];
				if (addRight <= b) {
					dp[i][j + 1][addRight] += dp[i][j][k];
					dp[i][j + 1][addRight] %= MOD;
				}
				int addLeft = pow(10, countDigits(k)) * d[j] + k;
				if (addLeft <= b) {
					dp[i][j + 1][addLeft] += dp[i][j][k];
					dp[i][j + 1][addLeft] %= MOD;
				}
			}
		}
	}
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j <= n; j++) {
			for (int k = 1; k <= b; k++) dp[i][j][k] += dp[i][j][k - 1];
		}
	}
	int q;
	cin >> q;
	for (int i = 0; i < q; i++) {
		int l, r;
		cin >> l >> r;
		--l;
		cout << (dp[l][r][b] - dp[l][r][a - 1] + MOD) % MOD << endl;
	}
}

Subtask 2: $A=B$, $T=O(N(\log_{10}B)^2)$

Let $d$ be the number of digits in $B$. For each $i\in [1,N]$ and $1\le l\le r\le d$, let $dp[i][l][r]$ denote the number of ways to form $B_{l\dots r}$ (the integer formed by concatenating the $l$th through $r$th digits of $B$) after moving over papers $1\dots i$. There are $O(Nd^2)$ such states, each of which can be computed in $O(1)$ time from $dp[i-1][l][r]$, $dp[i-1][l+1][r]$, and $dp[i][l][r-1]$. The answer for prefix $1\dots i$ is $dp[i][1][d]$.

Full credit: $T=O(N(\log_{10}B)^2)$

We subtract the number of ways to form an integer at most $A-1$ from the number of ways to form an integer at most $B$. To count the number of ways to form an integer at most $B$, we augment our DP states from the previous subtask with an additional flag that is equal to $0$, $1$, or $2$. Then $dp[i][l][r][0]$, $dp[i][l][r][1]$, and $dp[i][l][r][2]$ denote the number of ways to form an $r-l+1$-digit integer less than, equal to, or greater than $B_{l\dots r}$ after moving over papers $1\dots i$, respectively. The DP transitions are similar to the previous subtask. The answer for prefix $1\dots i$ is $dp[i][1][d][1]+dp[i][1][d][0]+\sum_{j=2}^d\sum_{k=0}^2dp[i][j][d][k]$. Here, $dp[i][1][d][1]$ is the number of ways to make a $d$-digit number equal to $B$, $dp[i][1][d][0]$ is the number of ways to make a $d$-digit number less than $B$, and $\sum_{k=0}^2dp[i][j][d][k]$ is the number of ways to make a $(d-j+1)$-digit number.

Danny Mittal's code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
 
public class Milkshake {
    public static final long MOD = 1000000007;
 
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer tokenizer = new StringTokenizer(in.readLine());
        int n = Integer.parseInt(tokenizer.nextToken());
        long a = Long.parseLong(tokenizer.nextToken());
        long b = Long.parseLong(tokenizer.nextToken());
        char[] digits = in.readLine().replace(" ", "").toCharArray();
 
        long[][] answersLeft = solve(("" + (a - 1L)).toCharArray(), digits);
        long[][] answersRight = solve(("" + b).toCharArray(), digits);
 
        StringBuilder out = new StringBuilder();
        for (int q = Integer.parseInt(in.readLine()); q > 0; q--) {
            tokenizer = new StringTokenizer(in.readLine());
            int l = Integer.parseInt(tokenizer.nextToken()) - 1;
            int r = Integer.parseInt(tokenizer.nextToken()) - 1;
            long answer = answersRight[l][r] - answersLeft[l][r];
            answer += MOD;
            answer %= MOD;
            out.append(answer).append('\n');
        }
        System.out.print(out);
    }
 
    static long[][] solve(char[] limit, char[] digits) {
        long[][] answers = new long[digits.length][digits.length];
        for (int j = 0; j < digits.length; j++) {
            long[][][] dp = new long[limit.length][limit.length][3];
            for (int k = j; k < digits.length; k++) {
                for (int x = 0; x < limit.length; x++) {
                    for (int y = limit.length - 1; y > x; y--) {
                        if (digits[k] > limit[x]) {
                            for (int c = 0; c <= 2; c++) {
                                dp[x][y][2] += dp[x + 1][y][c];
                            }
                        } else if (digits[k] == limit[x]) {
                            for (int c = 0; c <= 2; c++) {
                                dp[x][y][c] += dp[x + 1][y][c];
                            }
                        } else {
                            for (int c = 0; c <= 2; c++) {
                                dp[x][y][0] += dp[x + 1][y][c];
                            }
                        }
 
                        dp[x][y][2] += dp[x][y - 1][2];
                        dp[x][y][compare(digits[k], limit[y])] += dp[x][y - 1][1];
                        dp[x][y][0] += dp[x][y - 1][0];
 
                        for (int c = 0; c <= 2; c++) {
                            dp[x][y][c] %= MOD;
                        }
                    }
                }
                for (int x = 0; x < limit.length; x++) {
                    dp[x][x][compare(digits[k], limit[x])] += 2;
                }
 
                for (int x = 0; x < limit.length; x++) {
                    answers[j][k] += dp[x][limit.length - 1][0];
                    answers[j][k] += dp[x][limit.length - 1][1];
                    if (x != 0) {
                        answers[j][k] += dp[x][limit.length - 1][2];
                    }
                    answers[j][k] %= MOD;
                }
            }
        }
        return answers;
    }
 
    static int compare(char a, char b) {
        return Integer.signum(a - b) + 1;
    }
}