Problem p02616 (C++) — buggy submission, verdict: Runtime Error

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using ld = long double;
using pint = pair<int, int>;
using pll = pair<ll, ll>;
using pld = pair<ld, ld>;
const int INF = 1e9 + 7;
const ll LINF = 9223372036854775807;
const ll MOD = 1e9 + 7;
const ld PI = acos(-1);
const ld EPS = 1e-10; // 微調整用（EPSより小さいと0と判定など）

int ii() {
  int x;
  if (scanf("%d", &x) == 1)
    return x;
  else
    return 0;
}
long long il() {
  long long x;
  if (scanf("%lld", &x) == 1)
    return x;
  else
    return 0;
}
string is() {
  string x;
  cin >> x;
  return x;
}
char ic() {
  char x;
  cin >> x;
  return x;
}
void oi(int x) { printf("%d ", x); }
void ol(long long x) { printf("%lld ", x); }
void od_nosp(double x) { printf("%.15f", x); } // 古い問題用
void od(double x) { printf("%.15f ", x); }
// long doubleで受け取り、fをLfなどに変えて出力すると、変な数値が出る
// それをなんとかするには独自の出力を作らなければならなそう
void os(const string &s) { printf("%s ", s.c_str()); }
void oc(const char &c) { printf("%c ", c); }
#define o_map(v)                                                               \\
  {                                                                            \\
    cerr << #v << endl;                                                        \\
    for (const auto &xxx : v) {                                                \\
      cout << xxx.first << " " << xxx.second << "\\n";                          \\
    }                                                                          \\
  } // 動作未確認
void br() { putchar('\\n'); }

// #define gcd __gcd //llは受け取らない C++17~のgcdと違うので注意
// int lcm(int a, int b){return a / gcd(a, b) * b;}
#define begin_end(a) a.begin(), a.end() // sort(begin_end(vec));
#define REP(i, m, n) for (ll i = (ll)(m); i < (ll)(n); i++)
#define rep(i, n) REP(i, 0, n)
#define m_p(a, b) make_pair(a, b)
#define SORT_UNIQUE(c)                                                         \\
  (sort(c.begin(), c.end()),                                                   \\
   c.resize(distance(c.begin(), unique(c.begin(), c.end()))))
#define GET_POS(c, x) (lower_bound(c.begin(), c.end(), x) - c.begin())
#define p_b push_back
#define SZ(x) ((int)(x).size) // size()がunsignedなのでエラー避けに

// coutによるpairの出力（空白区切り）
template <typename T1, typename T2>
ostream &operator<<(ostream &s, const pair<T1, T2> &p) {
  return s << "(" << p.first << " " << p.second << ")";
}
// coutによるvectorの出力（空白区切り）
template <typename T> ostream &operator<<(ostream &s, const vector<T> &v) {
  int len = v.size();
  for (int i = 0; i < len; ++i) {
    s << v[i];
    if (i < len - 1)
      s << " "; //"\\t"に変えるとTabで見やすく区切る
  }
  return s;
}
// coutによる多次元vectorの出力（空白区切り）
template <typename T>
ostream &operator<<(ostream &s, const vector<vector<T>> &vv) {
  int len = vv.size();
  for (int i = 0; i < len; ++i) {
    s << vv[i] << endl;
  }
  return s;
}

// 最大値、最小値の更新。更新したor等しければtrueを返す
template <typename T> bool chmax(T &a, T b) { return (a = max(a, b)) == b; }
template <typename T> bool chmin(T &a, T b) { return (a = min(a, b)) == b; }

// 4近傍（上下左右） rep(i, 2) にすると右・下だけに進む
vector<int> dx_4 = {1, 0, -1, 0};
vector<int> dy_4 = {0, 1, 0, -1};

// -------- template end - //

// - library ------------- //

// --------- library end - //

int main() {

  ll N, K;
  cin >> N >> K;
  vector<ll> one_pluss;  // 0は入れない
  vector<ll> one_minuss; // 絶対値化している
  vector<ll> two_pluss;
  vector<ll> two_pluss_hitotsunozoku; // 一番大きいのを除いた版（奇数用）
  vector<ll> two_minuss;
  vector<ll> zeros;
  vector<ll> abss; // 絶対値を入れていく
  rep(i, N) {
    ll a = il();
    abss.p_b(abs(a));
    if (a < 0)
      one_minuss.p_b(-a);
    else if (a == 0)
      zeros.p_b(a);
    else
      one_pluss.p_b(a);
  }

  // absansは昇順にする
  sort(begin_end(abss));

  // 全て昇順にする
  sort(begin_end(one_pluss));
  reverse(begin_end(one_pluss));
  sort(begin_end(one_minuss));
  reverse(begin_end(one_minuss));

  // プラスのセットを作る
  // 自然と昇順になる
  ll idx = 0;
  while (idx + 2 <= one_pluss.size()) {
    two_pluss.p_b(one_pluss[idx] * one_pluss[idx + 1]);
    idx += 2;
  }

  // プラスのセット奇数版を作る
  // 自然と昇順になる
  idx = 1;
  while (idx + 2 <= one_pluss.size()) {
    two_pluss_hitotsunozoku.p_b(one_pluss[idx] * one_pluss[idx + 1]);
    idx += 2;
  }

  // マイナスのセットを作る
  // 自然と昇順になる
  idx = 0;
  while (idx + 2 <= one_minuss.size()) {
    two_minuss.p_b(one_minuss[idx] * one_minuss[idx + 1]);
    idx += 2;
  }

  ll tmpk = K; // Kは何度も再利用する
  ll onemin_idx = 0;
  ll oneplu_idx = 0;
  ll twomin_idx = 0;
  ll twoplu_idx = 0;
  ll tmpans = 1;

  // まず、答えをプラスにしようとする
  // 「余り1」の部分は必ずプラスを使わないといけない
  // それは、最も大きいプラスを使う
  if (K % 2 == 1) {
    if (one_pluss.size() == 0) {
      // プラスが無いのに奇数 -> 必ずゼロかマイナスになる
      if (zeros.size() != 0) {
        // 0が入っていれば0にして終わり
        cout << 0 << endl;
        return 0;
      } else {
        // 0が入っていない場合。
        // こうなると、負の値をどれだけ小さくするかという問題になる。
        // ということは、絶対値が小さいものから貪欲に選べばいい。
        ll absans = 1;
        rep(i, K) absans = absans * abss[i] % MOD;
        cout << MOD - absans << endl;
        return 0;
      }
    }
    // two_pluss_hitotsunozoku を使う
    tmpans *= one_pluss[0];
    oneplu_idx = 1;
    tmpk -= 1;

    // あとは2つずつ使っていく
    while (tmpk >= 2) {
      if (twomin_idx + 1 <= two_minuss.size() &&
          twoplu_idx + 1 <= two_pluss_hitotsunozoku.size()) {
        // どっちの符号も2つ使える場合
        if (two_minuss[twomin_idx] >= two_pluss_hitotsunozoku[twoplu_idx]) {
          tmpans = tmpans * (two_minuss[twomin_idx] % MOD) % MOD;
          twomin_idx++;
          tmpk -= 2;
        } else {
          tmpans = tmpans * (two_pluss_hitotsunozoku[twoplu_idx] % MOD) % MOD;
          twoplu_idx++;
          tmpk -= 2;
        }
      } else if (twomin_idx + 1 <= two_minuss.size()) {
        // twominだけ使える場合
        tmpans = tmpans * (two_minuss[twomin_idx] % MOD) % MOD;
        twomin_idx++;
        tmpk -= 2;
      } else if (twoplu_idx + 1 <= two_pluss_hitotsunozoku.size()) {
        // twopluだけ使える場合
        tmpans = tmpans * (two_pluss_hitotsunozoku[twoplu_idx] % MOD) % MOD;
        twoplu_idx++;
        tmpk -= 2;
      } else {
        // どちらも使えなくなってしまった場合
        // これはつまり、+が1つと-が1つ残ってしまったか、0が入っている場合
        if (zeros.size() != 0) {
          // 0が入っていれば0にして終わり
          cout << 0 << endl;
          return 0;
        } else {
          // 0が入っていない場合。
          // こうなると、負の値をどれだけ小さくするかという問題になる。
          // ということは、絶対値が小さいものから貪欲に選べばいい。
          ll absans = 1;
          rep(i, K) absans = absans * abss[i] % MOD;
          cout << MOD - absans << endl;
          return 0;
        }
      }
    }
    // 奇数なのにwhileループを抜けたら、うまくtmpansが作れたということ
    cout << tmpans << endl;
    return 0;
  }

  // 奇数の場合はこれで終わり、あとは偶数の場合
  tmpk = K; // Kは何度も再利用する
  onemin_idx = 0;
  oneplu_idx = 0;
  twomin_idx = 0;
  twoplu_idx = 0;
  tmpans = 1;

  // 偶数の場合はtwominとtwopluの大きい方からとっていく

  // 2つずつ使っていく
  while (tmpk >= 2) {
    if (twomin_idx + 1 <= two_minuss.size() &&
        twoplu_idx + 1 <= two_pluss.size()) {
      // どっちの符号も2つ使える場合
      if (two_minuss[twomin_idx] >= two_pluss[twoplu_idx]) {
        tmpans = tmpans * (two_minuss[twomin_idx] % MOD) % MOD;
        twomin_idx++;
        tmpk -= 2;
      } else {
        tmpans = tmpans * (two_pluss[twoplu_idx] % MOD) % MOD;
        twoplu_idx++;
        tmpk -= 2;
      }
    } else if (twomin_idx + 1 <= two_minuss.size()) {
      // twominだけ使える場合
      tmpans = tmpans * (two_minuss[twomin_idx] % MOD) % MOD;
      twomin_idx++;
      tmpk -= 2;
    } else if (twoplu_idx + 1 <= two_pluss.size()) {
      // twopluだけ使える場合
      tmpans = tmpans * (two_pluss[twoplu_idx] % MOD) % MOD;
      twoplu_idx++;
      tmpk -= 2;
    } else {
      // どちらも使えなくなってしまった場合
      // これはつまり、+が1つと-が1つ残ってしまったか、0が入っている場合
      if (zeros.size() != 0) {
        // 0が入っていれば0にして終わり
        cout << 0 << endl;
        return 0;
      } else {
        // 0が入っていない場合。
        // こうなると、負の値をどれだけ小さくするかという問題になる。
        // ということは、絶対値が小さいものから貪欲に選べばいい。
        ll absans = 1;
        rep(i, K) absans = absans * abss[i] % MOD;
        cout << MOD - absans << endl;
        return 0;
      }
    }
  }
  // whileループを抜けたら、うまくtmpansが作れたということ
  cout << tmpans << endl;
  return 0;
}

Error: 0

Fixed submission:

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
using ld = long double;
using pint = pair<int, int>;
using pll = pair<ll, ll>;
using pld = pair<ld, ld>;
const int INF = 1e9 + 7;
const ll LINF = 9223372036854775807;
const ll MOD = 1e9 + 7;
const ld PI = acos(-1);
const ld EPS = 1e-10; // 微調整用（EPSより小さいと0と判定など）

int ii() {
  int x;
  if (scanf("%d", &x) == 1)
    return x;
  else
    return 0;
}
long long il() {
  long long x;
  if (scanf("%lld", &x) == 1)
    return x;
  else
    return 0;
}
string is() {
  string x;
  cin >> x;
  return x;
}
char ic() {
  char x;
  cin >> x;
  return x;
}
void oi(int x) { printf("%d ", x); }
void ol(long long x) { printf("%lld ", x); }
void od_nosp(double x) { printf("%.15f", x); } // 古い問題用
void od(double x) { printf("%.15f ", x); }
// long doubleで受け取り、fをLfなどに変えて出力すると、変な数値が出る
// それをなんとかするには独自の出力を作らなければならなそう
void os(const string &s) { printf("%s ", s.c_str()); }
void oc(const char &c) { printf("%c ", c); }
#define o_map(v)                                                               \\
  {                                                                            \\
    cerr << #v << endl;                                                        \\
    for (const auto &xxx : v) {                                                \\
      cout << xxx.first << " " << xxx.second << "\\n";                          \\
    }                                                                          \\
  } // 動作未確認
void br() { putchar('\\n'); }

// #define gcd __gcd //llは受け取らない C++17~のgcdと違うので注意
// int lcm(int a, int b){return a / gcd(a, b) * b;}
#define begin_end(a) a.begin(), a.end() // sort(begin_end(vec));
#define REP(i, m, n) for (ll i = (ll)(m); i < (ll)(n); i++)
#define rep(i, n) REP(i, 0, n)
#define m_p(a, b) make_pair(a, b)
#define SORT_UNIQUE(c)                                                         \\
  (sort(c.begin(), c.end()),                                                   \\
   c.resize(distance(c.begin(), unique(c.begin(), c.end()))))
#define GET_POS(c, x) (lower_bound(c.begin(), c.end(), x) - c.begin())
#define p_b push_back
#define SZ(x) ((int)(x).size) // size()がunsignedなのでエラー避けに

// coutによるpairの出力（空白区切り）
template <typename T1, typename T2>
ostream &operator<<(ostream &s, const pair<T1, T2> &p) {
  return s << "(" << p.first << " " << p.second << ")";
}
// coutによるvectorの出力（空白区切り）
template <typename T> ostream &operator<<(ostream &s, const vector<T> &v) {
  int len = v.size();
  for (int i = 0; i < len; ++i) {
    s << v[i];
    if (i < len - 1)
      s << " "; //"\\t"に変えるとTabで見やすく区切る
  }
  return s;
}
// coutによる多次元vectorの出力（空白区切り）
template <typename T>
ostream &operator<<(ostream &s, const vector<vector<T>> &vv) {
  int len = vv.size();
  for (int i = 0; i < len; ++i) {
    s << vv[i] << endl;
  }
  return s;
}

// 最大値、最小値の更新。更新したor等しければtrueを返す
template <typename T> bool chmax(T &a, T b) { return (a = max(a, b)) == b; }
template <typename T> bool chmin(T &a, T b) { return (a = min(a, b)) == b; }

// 4近傍（上下左右） rep(i, 2) にすると右・下だけに進む
vector<int> dx_4 = {1, 0, -1, 0};
vector<int> dy_4 = {0, 1, 0, -1};

// -------- template end - //

// - library ------------- //

// --------- library end - //

int main() {

  ll N, K;
  cin >> N >> K;
  vector<ll> one_pluss;  // 0は入れない
  vector<ll> one_minuss; // 絶対値化している
  vector<ll> two_pluss;
  vector<ll> two_pluss_hitotsunozoku; // 一番大きいのを除いた版（奇数用）
  vector<ll> two_minuss;
  vector<ll> zeros;
  vector<ll> abss; // 絶対値を入れていく
  rep(i, N) {
    ll a = il();
    abss.p_b(abs(a));
    if (a < 0)
      one_minuss.p_b(-a);
    else if (a == 0)
      zeros.p_b(a);
    else
      one_pluss.p_b(a);
  }

  // absansは昇順にする
  sort(begin_end(abss));

  // 全て昇順にする
  sort(begin_end(one_pluss));
  reverse(begin_end(one_pluss));
  sort(begin_end(one_minuss));
  reverse(begin_end(one_minuss));

  // プラスのセットを作る
  // 自然と昇順になる
  ll idx = 0;
  while (one_pluss.size() != 0 && idx + 1 <= one_pluss.size() - 1) {
    two_pluss.p_b(one_pluss[idx] * one_pluss[idx + 1] % MOD);
    idx += 2;
  }

  // プラスのセット奇数版を作る
  // 自然と昇順になる
  idx = 1;
  while (one_pluss.size() != 0 && idx + 1 <= one_pluss.size() - 1) {
    two_pluss_hitotsunozoku.p_b(one_pluss[idx] * one_pluss[idx + 1] % MOD);
    idx += 2;
  }

  // マイナスのセットを作る
  // 自然と昇順になる
  idx = 0;
  while (idx + 1 <= one_minuss.size() - 1) {
    two_minuss.p_b(one_minuss[idx] * one_minuss[idx + 1] % MOD);
    idx += 2;
  }

  ll tmpk = K; // Kは何度も再利用する
  ll onemin_idx = 0;
  ll oneplu_idx = 0;
  ll twomin_idx = 0;
  ll twoplu_idx = 0;
  ll tmpans = 1;

  // まず、答えをプラスにしようとする
  // 「余り1」の部分は必ずプラスを使わないといけない
  // それは、最も大きいプラスを使う
  if (K % 2 == 1) {
    if (one_pluss.size() == 0) {
      if (zeros.size() != 0) {
        // 0が入っていれば0にして終わり
        cout << 0 << endl;
        return 0;
      } else {
        // 0が入っていない場合。
        // こうなると、負の値をどれだけ小さくするかという問題になる。
        // ということは、絶対値が小さいものから貪欲に選べばいい。
        ll absans = 1;
        rep(i, K) absans = absans * abss[i] % MOD;
        cout << -absans + MOD << endl;
        return 0;
      }
    }
    // two_pluss_hitotsunozoku を使う
    tmpans *= one_pluss[0];
    oneplu_idx = 1;
    tmpk -= 1;

    // あとは2つずつ使っていく
    while (tmpk >= 2) {
      if (twomin_idx <= two_minuss.size() - 1 &&
          twoplu_idx <= two_pluss_hitotsunozoku.size() - 1) {
        // どっちの符号も2つ使える場合
        if (two_minuss[twomin_idx] >= two_pluss_hitotsunozoku[twoplu_idx]) {
          tmpans = tmpans * two_minuss[twomin_idx] % MOD;
          twomin_idx++;
          tmpk -= 2;
        } else {
          tmpans = tmpans * two_pluss_hitotsunozoku[twoplu_idx] % MOD;
          twoplu_idx++;
          tmpk -= 2;
        }
      } else if (twomin_idx <= two_minuss.size() - 1) {
        // twominだけ使える場合
        tmpans = tmpans * two_minuss[twomin_idx] % MOD;
        twomin_idx++;
        tmpk -= 2;
      } else if (twoplu_idx <= two_pluss_hitotsunozoku.size() - 1) {
        // twopluだけ使える場合
        tmpans = tmpans * two_pluss_hitotsunozoku[twoplu_idx] % MOD;
        twoplu_idx++;
        tmpk -= 2;
      } else {
        // どちらも使えなくなってしまった場合
        // これはつまり、+が1つと-が1つ残ってしまったか、0が入っている場合
        if (zeros.size() != 0) {
          // 0が入っていれば0にして終わり
          cout << 0 << endl;
          return 0;
        } else {
          // 0が入っていない場合。
          // こうなると、負の値をどれだけ小さくするかという問題になる。
          // ということは、絶対値が小さいものから貪欲に選べばいい。
          ll absans = 1;
          rep(i, K) absans = absans * abss[i] % MOD;
          cout << -absans + MOD << endl;
          return 0;
        }
      }
    }
    // 奇数なのにwhileループを抜けたら、うまくtmpansが作れたということ
    cout << tmpans << endl;
    return 0;
  }

  // 奇数の場合はこれで終わり、あとは偶数の場合
  tmpk = K; // Kは何度も再利用する
  onemin_idx = 0;
  oneplu_idx = 0;
  twomin_idx = 0;
  twoplu_idx = 0;
  tmpans = 1;

  // 偶数の場合はtwominとtwopluの大きい方からとっていく

  // 2つずつ使っていく
  while (tmpk >= 2) {
    if (twomin_idx <= two_minuss.size() - 1 &&
        twoplu_idx <= two_pluss.size() - 1) {
      // どっちの符号も2つ使える場合
      if (two_minuss[twomin_idx] >= two_pluss[twoplu_idx]) {
        tmpans = tmpans * two_minuss[twomin_idx] % MOD;
        twomin_idx++;
        tmpk -= 2;
      } else {
        tmpans = tmpans * two_pluss[twoplu_idx] % MOD;
        twoplu_idx++;
        tmpk -= 2;
      }
    } else if (twomin_idx <= two_minuss.size() - 1) {
      // twominだけ使える場合
      tmpans = tmpans * two_minuss[twomin_idx] % MOD;
      twomin_idx++;
      tmpk -= 2;
    } else if (twoplu_idx <= two_pluss.size() - 1) {
      // twopluだけ使える場合
      tmpans = tmpans * two_pluss[twoplu_idx] % MOD;
      twoplu_idx++;
      tmpk -= 2;
    } else {
      // どちらも使えなくなってしまった場合
      // これはつまり、+が1つと-が1つ残ってしまったか、0が入っている場合
      if (zeros.size() != 0) {
        // 0が入っていれば0にして終わり
        cout << 0 << endl;
        return 0;
      } else {
        // 0が入っていない場合。
        // こうなると、負の値をどれだけ小さくするかという問題になる。
        // ということは、絶対値が小さいものから貪欲に選べばいい。
        ll absans = 1;
        rep(i, K) absans = absans * abss[i] % MOD;
        cout << -absans << endl;
        return 0;
      }
    }
  }
  // whileループを抜けたら、うまくtmpansが作れたということ
  cout << tmpans << endl;
  return 0;
}