Problem p02888 (C++)

Buggy submission (status: Time Limit Exceeded):
#include <bits/stdc++.h>

using namespace std;

#define M_DEBUG if (0)

/******************************************************************************
        typedef
******************************************************************************/
typedef unsigned long long int ullint;
typedef long long int llint;

/*! 与えられた3つの非負整数 a, b, c を辺の長さとする三角形が存在するか */
bool isTriangle(ullint a, ullint b, ullint c) {
  return ((a < b + c) && (b < a + c) && (c < a + b));
}

/*!
        nCk の全組み合わせに対して処理 f を行う関数 foreach_comb と、
        再帰的に組み合わせを探索する内部関数 recursive_comb
*/
void recursive_comb(ullint *indexes, llint s, ullint rest,
                    function<void(ullint *)> f) {
  M_DEBUG cout << "s: " << s << " rest: " << rest << endl;
  if (rest == 0) {
    f(indexes);
  } else {
    if (s < 0)
      return;
    recursive_comb(indexes, s - 1, rest, f);
    indexes[rest - 1] = s;
    recursive_comb(indexes, s - 1, rest - 1, f);
  }
}

// nCkの組み合わせに対して処理を実行する
void foreach_comb(ullint n, llint k, function<void(ullint *)> f) {
  // int indexes[k];
  ullint *indexes = new ullint[k];
  recursive_comb(indexes, n - 1, k, f);
  delete indexes;
}

/******************************************************************************
        Main
******************************************************************************/
int main() {
  ullint n = 0;
  cin >> n;
  assert(n >= 3 && n <= 2000);

  vector<ullint> l_list;
  for (ullint i = 0; i < n; i++) {
    ullint l_i = 0;
    cin >> l_i;
    assert(l_i >= 1 && l_i <= 1000);
    l_list.push_back(l_i);
  }

  ullint answer = 0; // foreach_comb() で加算するためしかたなく
  foreach_comb(n, 3, [&answer, &l_list](ullint *indexes) {
    M_DEBUG cout << "isTriangle(" << l_list[indexes[0]] << ','
                 << l_list[indexes[1]] << ',' << l_list[indexes[2]] << ") = "
                 << isTriangle(l_list[indexes[0]], l_list[indexes[1]],
                               l_list[indexes[2]])
                 << endl;
    if (isTriangle(l_list[indexes[0]], l_list[indexes[1]],
                   l_list[indexes[2]])) {
      answer++;
    }
  });
  cout << answer << endl;

  return 0;
}


Fixed submission:
#include <bits/stdc++.h>

using namespace std;

#define M_DEBUG if (0)

/******************************************************************************
        typedef
******************************************************************************/
typedef unsigned long long int ullint;
typedef long long int llint;

/*! 与えられた3つの非負整数 a, b, c を辺の長さとする三角形が存在するか */
bool isTriangle(ullint a, ullint b, ullint c) {
  return ((a < b + c) && (b < a + c) && (c < a + b));
}

/*!
        nCk の全組み合わせに対して処理 f を行う関数 foreach_comb と、
        再帰的に組み合わせを探索する内部関数 recursive_comb
*/
void recursive_comb(ullint *indexes, llint s, ullint rest,
                    function<void(ullint *)> f) {
  M_DEBUG cout << "s: " << s << " rest: " << rest << endl;
  if (rest == 0) {
    f(indexes);
  } else {
    if (s < 0)
      return;
    recursive_comb(indexes, s - 1, rest, f);
    indexes[rest - 1] = s;
    recursive_comb(indexes, s - 1, rest - 1, f);
  }
}

// nCkの組み合わせに対して処理を実行する
void foreach_comb(ullint n, llint k, function<void(ullint *)> f) {
  // int indexes[k];
  ullint *indexes = new ullint[k];
  recursive_comb(indexes, n - 1, k, f);
  delete indexes;
}

/******************************************************************************
        Main
******************************************************************************/
int main() {
  ullint n = 0;
  cin >> n;
  assert(n >= 3 && n <= 2000);

  vector<ullint> l_list;
  for (ullint i = 0; i < n; i++) {
    ullint l_i = 0;
    cin >> l_i;
    assert(l_i >= 1 && l_i <= 1000);
    l_list.push_back(l_i);
  }

  ullint answer = 0;
  // これでは計算量がO(N^3)になるのでNG
  // foreach_comb(n, 3, [&answer, &l_list](ullint *indexes) {
  // 	M_DEBUG cout << "isTriangle(" << l_list[indexes[0]] << ',' <<
  // l_list[indexes[1]] << ',' << l_list[indexes[2]]  << ") = " <<
  // isTriangle(l_list[indexes[0]], l_list[indexes[1]], l_list[indexes[2]]) <<
  // endl; 	if (isTriangle(l_list[indexes[0]], l_list[indexes[1]],
  // l_list[indexes[2]])) { 		answer++;
  // 	}
  // });

  sort(l_list.begin(), l_list.end()); // 昇順
  M_DEBUG {
    for (auto a : l_list) {
      cout << a << " ";
    }
    cout << endl;
  }
  foreach_comb(n, 2, [n, &l_list, &answer](ullint *indexes) {
    auto itr = lower_bound(
        l_list.begin(), l_list.end(),
        l_list[indexes[0]] +
            l_list[indexes[1]]); // a + b > c で第3の辺の探索数を削る
    ullint ng = distance(itr, l_list.end());
    M_DEBUG cout << l_list[indexes[0]] << ',' << l_list[indexes[1]];

    //  b + c > a(1), a + c > b(2) をチェックする

    // ...が、次のような愚直なチェックは不要。
    // while (itr != l_list.begin()) {
    // 	if (*itr <= l_list[indexes[1]]) break;  // 既にチェックした組み合わせ
    // 	M_DEBUG cout << "isTriangle(" << l_list[indexes[0]] << ',' <<
    // l_list[indexes[1]] << ',' << *itr << ") = " <<
    // isTriangle(l_list[indexes[0]], l_list[indexes[1]], *itr) << endl; 	if
    // (isTriangle(l_list[indexes[0]], l_list[indexes[1]], *itr)) { 		answer++;
    // 	}
    // 	itr--;
    // }

    // なぜなら a < b なので a < b + c が成り立つから(1)はチェックしなくてよい。
    // あとは c > b - a であれば条件(2)を満たす。なので、lower_bound( , , b - a)
    // から c までの要素はすべて(2)を満たす。 しかし b
    // より前の要素を数えてしまうと、既に a と b
    // の組み合わせで調べているので二重カウントしてしまう。 よって b
    // の次から数えればよく、その個数は N - (n - index of c) - (indexes[1] +1)
    answer += n - indexes[1] - 1 - ng;
    M_DEBUG {
      itr--;
      cout << ',' << *itr << endl;
    }
  });
  cout << answer << endl;

  return 0;
}