Problem:
ジグザグ数 (Zig-Zag Numbers)

問題

正の整数を (先頭に 0 をつけずに) 10 進法で書いて桁の数字を順番に見ていくと増加と減少を交互に繰り返しているとき，その数は「ジグザグ数」であると呼ぶことにしよう．たとえば，2947 は桁の数字が 2 → 9 → 4 → 7 と，増加 → 減少 → 増加 の順になっているのでジグザグ数である．また，71946 は 減少 → 増加 → 減少 → 増加 の順なのでジグザグ数である．一方，123 や 71446 や 71442 や 88 はジグザグ数ではない．なお，1 桁の正の整数はジグザグ数であると考えることとする．

A 以上 B 以下の M の倍数のうち，ジグザグ数の個数を 10000 で割った余りを求めるプログラムを作成せよ．

入力

入力は 3 行からなり，1 行に 1 つずつ正の整数が書かれている．

1 行目の整数は A を，2 行目の整数は B を，3 行目の整数は M を表す．これらは 1 ≦ A ≦ B ≦ 10500，1 ≦ M ≦ 500 を満たす．

※ A や B の値は，通常の整数を表すデータ型には収まらない可能性があることに注意せよ．

出力

A 以上 B 以下の M の倍数のうち，ジグザグ数の個数を 10000 で割った余りを 1 行で出力せよ．

入出力例

入力例 1

100
200
5

出力例 1

13

入出力例 1 において，100 以上 200 以下の 5 の倍数であるジグザグ数は，105, 120, 130, 140, 150, 160, 165, 170, 175, 180, 185, 190, 195 の 13 個である．

入力例 2

6
1234567
3

出力例 2

246

入出力例 2 において，6 以上 1234567 以下の 3 の倍数であるジグザグ数は 50246 個あるので，それを 10000 で割った余りである 246 を出力する．

問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in JAVA (judge verdict: Memory Limit Exceeded)


import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) throws IOException {

		new Main().run();
	}

	private void run() throws IOException {
		Scanner scanner = new Scanner(System.in);
		String a = scanner.nextBigInteger().subtract(BigInteger.ONE).toString();
		String b = scanner.next();
		M = scanner.nextInt();
		dp = new int[b.length() + 1][M][10][3][2];
		for (int[][][][] dp1 : dp)
			for (int[][][] dp2 : dp1)
				for (int[][] dp3 : dp2)
					for (int[] dp4 : dp3)
						Arrays.fill(dp4, -1);
		System.out.println((slove(b) - slove(a) + mod) % mod);
	}

	private int slove(String s) {
		n = s.length();
		num = new int[n + 1];
		for (int i = 0; i < n; i++)
			num[n - i - 1] = s.charAt(i) - '0';

		int res = 0;
		for (int i = n - 1; i >= 0; i--)
			for (int j = 1; j <= (i == n - 1 ? num[i] : 9); j++) {
				res = (res + slove(i, j % M, j, 2, i == n - 1 && j == num[i]))
						% mod;

			}

		return res % mod;
	}

	private int slove(int p, int m, int pre, int s, boolean b) {
		if (p == 0)
			return m == 0 ? 1 : 0;
		if (dp[p][m][pre][s][b ? 1 : 0] != -1)
			return dp[p][m][pre][s][b ? 1 : 0];
		int ans = 0;

		if (s == 0 || s == 2) {
			int size = (b ? num[p - 1] : 9);
			for (int i = pre + 1; i <= size; i++)
				ans += slove(p - 1, (m * 10 + i) % M, i, 1, b
						&& i == num[p - 1]);
		}
		if (s == 1 || s == 2) {
			int size = Math.min(b ? num[p - 1] : 9, pre - 1);
			for (int i = 0; i <= size; i++)
				ans += slove(p - 1, (m * 10 + i) % M, i, 0, b
						&& i == num[p - 1]);
		}
		return dp[p][m][pre][s][b ? 1 : 0] = ans % mod;

	}

	int M, n;
	int mod = 10000;
	int[] num;
	int[][][][][] dp;
}