Problem p01699 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;

#define NUM 7

enum Type {
  A,
  B,
};

struct Info {
  int low, high;
};

int N;
int POW_2[7], POW_10[7];
int MIN_LEN[2], MAX_LEN[2];
Info info[NUM];
ll dp[2][16];
map<int, bool> MAP[2];
ll num_list[2][NUM];              // 長さ別の数字のリスト
int head_min[NUM], head_max[NUM]; // 先頭[len]文字の最小・最大数(A用)
int tail_min[NUM], tail_max[NUM]; // 末尾[len]文字の最小・最大長(B用)
int head_table[1000][4]
              [4]; // head_table[common_part_len][先頭から切り出す桁数] = 数

vector<int> state_table[16];
vector<int> HEAD[NUM], TAIL[NUM];

int get_length(ll num) {

  if (num == 0) {
    return 1;
  }

  int ret = 0;

  while (num > 0) {
    num /= 10;
    ret++;
  }

  return ret;
}

// numの先頭len文字の数字を作る
int substr_front(int num, int tmp_len, int cut_len) {

  num /= POW_10[tmp_len - cut_len];
  return num;
}

// numの末尾len文字の数字を作る
int substr_tail(int num, int len) {
  num %= POW_10[len];
  return num;
}

// 数字を作る
void recursive(Type type, int index, int finish_index, int num) {

  int tmp_length;

  if (index == finish_index + 1) {

    auto at = MAP[type].find(num);
    if (at != MAP[type].end())
      return; // 登録済

    MAP[type][num] = true;
    tmp_length = get_length(num);
    MAX_LEN[type] = max(MAX_LEN[type], tmp_length);
    MIN_LEN[type] = min(MIN_LEN[type], tmp_length);
    num_list[type][tmp_length] += 1;

    int tmp;

    if (type == A) {

      for (int i = 1; i <= tmp_length; i++) {
        tmp = substr_front(num, tmp_length, i);
        HEAD[i].push_back(tmp);
      }

    } else {

      for (int i = 1; i <= tmp_length; i++) {
        tmp = substr_tail(num, i);
        TAIL[i].push_back(tmp);
      }
    }
    return;
  }

  int tmp_num, tmp_len, next_num;
  // 次の数字を全探索する
  for (int i = info[index].low; i <= info[index].high; i++) {
    tmp_len = get_length(i);
    next_num = num * POW_10[tmp_len] + i;
    recursive(type, index + 1, finish_index, next_num);
  }
}

void init_dp() {

  for (int i = 0; i < 16; i++) {
    dp[A][i] = 0;
    dp[B][i] = 0;
  }
}

bool rangeCheck(int num_B) {
  if (num_B >= MIN_LEN[B] && num_B <= MAX_LEN[B]) {
    return true;
  } else {
    return false;
  }
}

void func() {

  MAP[A].clear();
  MAP[B].clear();

  for (int i = 1; i < NUM; i++) {
    num_list[A][i] = 0;
    num_list[B][i] = 0;

    head_min[i] = BIG_NUM;
    tail_min[i] = BIG_NUM;

    head_max[i] = 0;
    tail_max[i] = 0;

    HEAD[i].clear();
    TAIL[i].clear();
  }

  for (int i = 0; i < N; i++) {
    scanf("%d %d", &info[i].low, &info[i].high);
  }

  MIN_LEN[A] = BIG_NUM;
  MIN_LEN[B] = BIG_NUM;

  MAX_LEN[A] = -1;
  MAX_LEN[B] = -1;

  // 前半・後半でグループを作り、あらかじめ数字を作っておく
  recursive(A, 0, N / 2 - 1, 0);
  recursive(B, N / 2, N - 1, 0);

  for (int i = 1; i <= 5; i++) {
    sort(HEAD[i].begin(), HEAD[i].end());
    HEAD[i].erase(unique(HEAD[i].begin(), HEAD[i].end()), HEAD[i].end());

    sort(TAIL[i].begin(), TAIL[i].end());
    TAIL[i].erase(unique(TAIL[i].begin(), TAIL[i].end()), TAIL[i].end());
  }

  int common_part_len; // 中央部の長さ:0～3であるはず
  int common_max;

  ll ans = 0;

  bool is_First;
  int A_min, A_max, B_min, B_max;
  int tmp_A, tmp_B, add_A_len, add_B_len, part_num, tmp_num;
  int num_bit, a_len, b_len, tmp_state;

  bool FLG;

  for (int total_len = MIN_LEN[A] + MIN_LEN[B];
       total_len <= MAX_LEN[A] + MAX_LEN[B]; total_len++) {

    // 中央部の長さと、中央部の数の上限を求める
    is_First = true;
    for (int a = MIN_LEN[A]; a <= min(MAX_LEN[A], total_len); a++) {

      if (!rangeCheck(total_len - a))
        continue; // Bの長さが不適ならSKIP

      if (is_First) {

        A_min = a;
        A_max = a;

        B_min = total_len - a;
        B_max = total_len - a;
        is_First = false;
      } else {
        A_max = a;
        B_min = total_len - a;
      }
    }

    common_part_len = A_max - A_min;

    if (common_part_len == 0) { // 共通部の長さが0

      // 包除原理の適用不要
      ll count_A = num_list[A][A_min];
      ll count_B = num_list[B][B_max];

      ans += count_A * count_B;

      continue;
    }

    // ★共通部として考えられる、長さcommon_part_lenの数字を全探索★
    common_max = POW_10[common_part_len] - 1;

    for (int common_num = 0; common_num <= common_max; common_num++) {

      // ★★★★共通の数字別に数え上げる★★★★
      init_dp();

      a_len = A_min;

      // Aの数字全探索
      for (int a = 0; a < HEAD[a_len].size(); a++) {

        int tmp_A = HEAD[a_len][a];

        tmp_state = 0;

        for (int loop = 0; loop < common_part_len + 1; loop++) {

          // ★common_numのうち、先頭common_part_len-loopがcommon_numにおけるAの専有部
          add_A_len = common_part_len - loop;

          if (add_A_len == 0) {
            auto at = MAP[A].find(tmp_A);
            if (at == MAP[A].end()) { // tmp_Aが存在しない数字なら不可
              // Do nothing
            } else {
              tmp_state += POW_2[loop];
            }
            continue;
          }

          part_num = head_table[common_num][common_part_len][add_A_len];

          // 先頭から現れるべき数字
          tmp_num = tmp_A * POW_10[add_A_len] + part_num;

          auto at = MAP[A].find(tmp_num);

          if (at ==
              MAP[A]
                  .end()) { // 条件を満たす数字がないので、今回のsateは計上しない
            // Do nothing
          } else {
            tmp_state += POW_2[loop];
          }
        }

        for (int i = 0; i < state_table[tmp_state].size(); i++) {
          dp[A][state_table[tmp_state][i]] += 1;
        }
      }

      b_len = B_min;

      // Bの数字全探索
      for (int b = 0; b < TAIL[b_len].size();
           b++) { // ★★Bは左に数字を付け加えるので0スタート★★

        tmp_B = TAIL[b_len][b];

        tmp_state = 0;

        for (int loop = 0; loop < common_part_len + 1; loop++) {
          // ★common_numのうち、末尾loopがcommon_numにおけるBの専有部

          add_B_len = loop;

          if (add_B_len == 0) {
            if (tmp_B < POW_10[b_len - 1]) { // 先頭0は不可
              continue;
            }
            auto at = MAP[B].find(tmp_B);
            if (at == MAP[B].end()) { // 作れない数字は不可
              // Do nothing
            } else {
              tmp_state += POW_2[loop];
            }
            continue;
          }

          part_num = substr_tail(common_num, add_B_len);

          if (part_num < POW_10[add_B_len - 1]) { // ★★先頭0は不可★★
            continue;
          }

          // Bとして現れるべき数字
          tmp_num = part_num * POW_10[b_len] + tmp_B;

          auto at = MAP[B].find(tmp_num);

          if (at ==
              MAP[B]
                  .end()) { // 条件を満たす数字がないので、今回のsateは計上しない
            // Do nothing
          } else {
            tmp_state += POW_2[loop];
          }
        }

        for (int i = 0; i < state_table[tmp_state].size(); i++) {
          dp[B][state_table[tmp_state][i]] += 1;
        }
      }

      // ★中央の数字ごとにdp集計<包除原理>★
      for (int state = 1; state < POW_2[common_part_len + 1]; state++) {

        num_bit = 0;

        for (int loop = 0; loop < common_part_len + 1; loop++) {
          if (state & (1 << loop)) {
            num_bit++;
          }
        }

        if (num_bit % 2 == 1) {

          ans += dp[A][state] * dp[B][state];

        } else {

          ans -= dp[A][state] * dp[B][state];
        }
      }
    }
  }
  printf("%lld\\n", ans);
}

// 1～15の状態の、サブstateをあらかじめ求める関数
void make_state_table() {

  bool check[4];
  bool FLG;

  for (int state = 1; state < 16; state++) {
    for (int loop = 0; loop < 4; loop++) {
      if (state & (1 << loop)) {
        check[loop] = true;
      } else {
        check[loop] = false;
      }
    }

    for (int add_state = 1; add_state < 16; add_state++) {
      FLG = true;
      for (int loop = 0; loop < 4; loop++) {
        if (add_state & (1 << loop)) {
          if (!check[loop]) {
            FLG = false;
            break;
          }
        }
      }
      if (FLG) {
        state_table[state].push_back(
            add_state); // stateにおいて、add_stateがsub stateとして存在
      }
    }
  }
}

// 0～999の数字の、先頭1～3桁の数の事前計算
void makeTable() {

  int work[3], tmp;
  stack<int> S;

  for (int num = 0; num <= 999; num++) { // 数字

    tmp = num;

    for (int i = 2; i >= 0; i--) { // 右詰めで数字を配置
      work[i] = tmp % 10;
      tmp /= 10;
    }

    for (int common_part_len = 1; common_part_len <= 3;
         common_part_len++) { // 全体の桁数
      for (int head_len = 1; head_len <= common_part_len; head_len++) {
        tmp = 0;
        for (int k = 0; k < head_len; k++) {
          tmp = 10 * tmp + work[3 - common_part_len + k];
        }
        head_table[num][common_part_len][head_len] = tmp;
      }
    }
  }
}

int main() {

  for (int i = 0; i < 7; i++) {
    POW_2[i] = pow(2, i);
    POW_10[i] = pow(10, i);
  }

  makeTable();

  make_state_table();

  while (true) {
    scanf("%d", &N);
    if (N == 0)
      break;

    func();
  }

  return 0;
}


Error: TLE

Fixed submission:

#include <bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;

#define NUM 7

enum Type {
  A,
  B,
};

struct Info {
  int low, high;
};

int N;
int POW_2[7], POW_10[7];
int MIN_LEN[2], MAX_LEN[2];
Info info[NUM];
ll dp[2][16];
map<int, bool> MAP[2];
ll num_list[2][NUM];              // 長さ別の数字のリスト
int head_min[NUM], head_max[NUM]; // 先頭[len]文字の最小・最大数(A用)
int tail_min[NUM], tail_max[NUM]; // 末尾[len]文字の最小・最大長(B用)
int head_table[1000][4]
              [4]; // head_table[common_part_len][先頭から切り出す桁数] = 数

vector<int> state_table[16];
vector<int> HEAD[NUM];

int get_length(ll num) {

  if (num == 0) {
    return 1;
  }

  int ret = 0;

  while (num > 0) {
    num /= 10;
    ret++;
  }

  return ret;
}

// numの先頭len文字の数字を作る
int substr_front(int num, int tmp_len, int cut_len) {

  num /= POW_10[tmp_len - cut_len];
  return num;
}

// numの末尾len文字の数字を作る
int substr_tail(int num, int len) {
  num %= POW_10[len];
  return num;
}

// 数字を作る
void recursive(Type type, int index, int finish_index, int num) {

  int tmp_length;

  if (index == finish_index + 1) {

    auto at = MAP[type].find(num);
    if (at != MAP[type].end())
      return; // 登録済

    MAP[type][num] = true;
    tmp_length = get_length(num);
    MAX_LEN[type] = max(MAX_LEN[type], tmp_length);
    MIN_LEN[type] = min(MIN_LEN[type], tmp_length);
    num_list[type][tmp_length] += 1;

    int tmp;

    if (type == A) {

      for (int i = 1; i <= tmp_length; i++) {
        tmp = substr_front(num, tmp_length, i);
        HEAD[i].push_back(tmp);
      }

    } else {

      for (int i = 1; i <= tmp_length; i++) {
        tmp = substr_tail(num, i);
        tail_max[i] = max(tail_max[i], tmp);
        tail_min[i] = min(tail_min[i], tmp);
      }
    }
    return;
  }

  int tmp_num, tmp_len, next_num;
  // 次の数字を全探索する
  for (int i = info[index].low; i <= info[index].high; i++) {
    tmp_len = get_length(i);
    next_num = num * POW_10[tmp_len] + i;
    recursive(type, index + 1, finish_index, next_num);
  }
}

void init_dp() {

  for (int i = 0; i < 16; i++) {
    dp[A][i] = 0;
    dp[B][i] = 0;
  }
}

bool rangeCheck(int num_B) {
  if (num_B >= MIN_LEN[B] && num_B <= MAX_LEN[B]) {
    return true;
  } else {
    return false;
  }
}

void func() {

  MAP[A].clear();
  MAP[B].clear();

  for (int i = 1; i < NUM; i++) {
    num_list[A][i] = 0;
    num_list[B][i] = 0;

    head_min[i] = BIG_NUM;
    tail_min[i] = BIG_NUM;

    head_max[i] = 0;
    tail_max[i] = 0;

    HEAD[i].clear();
  }

  for (int i = 0; i < N; i++) {
    scanf("%d %d", &info[i].low, &info[i].high);
  }

  MIN_LEN[A] = BIG_NUM;
  MIN_LEN[B] = BIG_NUM;

  MAX_LEN[A] = -1;
  MAX_LEN[B] = -1;

  // 前半・後半でグループを作り、あらかじめ数字を作っておく
  recursive(A, 0, N / 2 - 1, 0);
  recursive(B, N / 2, N - 1, 0);

  for (int i = 1; i <= 5; i++) {
    sort(HEAD[i].begin(), HEAD[i].end());
    HEAD[i].erase(unique(HEAD[i].begin(), HEAD[i].end()), HEAD[i].end());
  }

  int common_part_len; // 中央部の長さ:0～3であるはず
  int common_max;

  ll ans = 0;

  bool is_First;
  int A_min, A_max, B_min, B_max;
  int tmp_A, tmp_B, add_A_len, add_B_len, part_num, tmp_num;
  int num_bit, a_len, b_len, tmp_state;

  bool FLG;

  for (int total_len = MIN_LEN[A] + MIN_LEN[B];
       total_len <= MAX_LEN[A] + MAX_LEN[B]; total_len++) {

    // 中央部の長さと、中央部の数の上限を求める
    is_First = true;
    for (int a = MIN_LEN[A]; a <= min(MAX_LEN[A], total_len); a++) {

      if (!rangeCheck(total_len - a))
        continue; // Bの長さが不適ならSKIP

      if (is_First) {

        A_min = a;
        A_max = a;

        B_min = total_len - a;
        B_max = total_len - a;
        is_First = false;
      } else {
        A_max = a;
        B_min = total_len - a;
      }
    }

    common_part_len = A_max - A_min;

    if (common_part_len == 0) { // 共通部の長さが0

      // 包除原理の適用不要
      ll count_A = num_list[A][A_min];
      ll count_B = num_list[B][B_max];

      ans += count_A * count_B;

      continue;
    }

    // ★共通部として考えられる、長さcommon_part_lenの数字を全探索★
    common_max = POW_10[common_part_len] - 1;

    for (int common_num = 0; common_num <= common_max; common_num++) {

      // ★★★★共通の数字別に数え上げる★★★★
      init_dp();

      a_len = A_min;

      // Aの数字全探索
      for (int a = 0; a < HEAD[a_len].size(); a++) {

        int tmp_A = HEAD[a_len][a];

        tmp_state = 0;

        for (int loop = 0; loop < common_part_len + 1; loop++) {

          // ★common_numのうち、先頭common_part_len-loopがcommon_numにおけるAの専有部
          add_A_len = common_part_len - loop;

          if (add_A_len == 0) {
            auto at = MAP[A].find(tmp_A);
            if (at == MAP[A].end()) { // tmp_Aが存在しない数字なら不可
              // Do nothing
            } else {
              tmp_state += POW_2[loop];
            }
            continue;
          }

          part_num = head_table[common_num][common_part_len][add_A_len];

          // 先頭から現れるべき数字
          tmp_num = tmp_A * POW_10[add_A_len] + part_num;

          auto at = MAP[A].find(tmp_num);

          if (at ==
              MAP[A]
                  .end()) { // 条件を満たす数字がないので、今回のsateは計上しない
            // Do nothing
          } else {
            tmp_state += POW_2[loop];
          }
        }

        for (int i = 0; i < state_table[tmp_state].size(); i++) {
          dp[A][state_table[tmp_state][i]] += 1;
        }
      }

      b_len = B_min;

      // Bの数字全探索
      for (int tmp_B = tail_min[b_len]; tmp_B <= tail_max[b_len];
           tmp_B++) { // ★★Bは左に数字を付け加えるので0スタート★★

        tmp_state = 0;

        for (int loop = 0; loop < common_part_len + 1; loop++) {
          // ★common_numのうち、末尾loopがcommon_numにおけるBの専有部

          add_B_len = loop;

          if (add_B_len == 0) {
            if (tmp_B < POW_10[b_len - 1]) { // 先頭0は不可
              continue;
            }
            auto at = MAP[B].find(tmp_B);
            if (at == MAP[B].end()) { // 作れない数字は不可
              // Do nothing
            } else {
              tmp_state += POW_2[loop];
            }
            continue;
          }

          part_num = substr_tail(common_num, add_B_len);

          if (part_num < POW_10[add_B_len - 1]) { // ★★先頭0は不可★★
            continue;
          }

          // Bとして現れるべき数字
          tmp_num = part_num * POW_10[b_len] + tmp_B;

          auto at = MAP[B].find(tmp_num);

          if (at ==
              MAP[B]
                  .end()) { // 条件を満たす数字がないので、今回のsateは計上しない
            // Do nothing
          } else {
            tmp_state += POW_2[loop];
          }
        }

        for (int i = 0; i < state_table[tmp_state].size(); i++) {
          dp[B][state_table[tmp_state][i]] += 1;
        }
      }

      // ★中央の数字ごとにdp集計<包除原理>★
      for (int state = 1; state < POW_2[common_part_len + 1]; state++) {

        num_bit = 0;

        for (int loop = 0; loop < common_part_len + 1; loop++) {
          if (state & (1 << loop)) {
            num_bit++;
          }
        }

        if (num_bit % 2 == 1) {

          ans += dp[A][state] * dp[B][state];

        } else {

          ans -= dp[A][state] * dp[B][state];
        }
      }
    }
  }
  printf("%lld\\n", ans);
}

// 1～15の状態の、サブstateをあらかじめ求める関数
void make_state_table() {

  bool check[4];
  bool FLG;

  for (int state = 1; state < 16; state++) {
    for (int loop = 0; loop < 4; loop++) {
      if (state & (1 << loop)) {
        check[loop] = true;
      } else {
        check[loop] = false;
      }
    }

    for (int add_state = 1; add_state < 16; add_state++) {
      FLG = true;
      for (int loop = 0; loop < 4; loop++) {
        if (add_state & (1 << loop)) {
          if (!check[loop]) {
            FLG = false;
            break;
          }
        }
      }
      if (FLG) {
        state_table[state].push_back(
            add_state); // stateにおいて、add_stateがsub stateとして存在
      }
    }
  }
}

// 0～999の数字の、先頭1～3桁の数の事前計算
void makeTable() {

  int work[3], tmp;
  stack<int> S;

  for (int num = 0; num <= 999; num++) { // 数字

    tmp = num;

    for (int i = 2; i >= 0; i--) { // 右詰めで数字を配置
      work[i] = tmp % 10;
      tmp /= 10;
    }

    for (int common_part_len = 1; common_part_len <= 3;
         common_part_len++) { // 全体の桁数
      for (int head_len = 1; head_len <= common_part_len; head_len++) {
        tmp = 0;
        for (int k = 0; k < head_len; k++) {
          tmp = 10 * tmp + work[3 - common_part_len + k];
        }
        head_table[num][common_part_len][head_len] = tmp;
      }
    }
  }
}

int main() {

  for (int i = 0; i < 7; i++) {
    POW_2[i] = pow(2, i);
    POW_10[i] = pow(10, i);
  }

  makeTable();

  make_state_table();

  while (true) {
    scanf("%d", &N);
    if (N == 0)
      break;

    func();
  }

  return 0;
}