競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 173E問題

ABC173E

解法

マイナスの個数に気を付けて貪欲にとる. 最初は 0 は除外して考えると楽. 今回は,0 を正に含めば大丈夫.
\(K\)個の積を 0 以上にできるかで場合分け. 0 以上に出来るときは abs が大きくなる様にとればよいし, 出来ないときは abs が小さくなる様にとればよい. 0 以上に出来るかどうかは,マイナスから2個ずつ選ぶ貪欲法で判定できる. 2個ずつペアにしていき,残った物を 0 以上から選べ場良い.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n,k;
  cin >> n >> k;
  vll a(n); rep(i,n) { cin >> a[i]; }


  vll ap, am;
  rep(i,n){
    if(a[i] >= 0) ap.push_back(a[i]);
    else am.push_back(a[i]);
  }
 
  bool ok = false; // true iff (a から丁度 k個選んで,積を 0以上に出来る)
  if(ap.size() > 0){
    if(n == k) ok = (am.size()%2==0);
    else ok = true;
  }else {
    ok = (k%2==0);
  }

  mint ans = 1;
  ll x = min(k/2*2, (ll)am.size()/2*2); // 積を 0以上にできるかの判定は,マイナスから 2個ずつ貪欲にとれば良い.
  assert(ok == (ap.size() >= k-x));
  if(ok){
    sort(all(ap));
    sort(am.rbegin(), am.rend()); // sort(all(am), greater<ll>());

    if(k%2) {
      ans *= ap.back();
      ap.pop_back(); // do not forget ... [*]
      k--;
    }

    vll v;
    rep(_,2){
      while(ap.size() >= 2){
        ll x = 1;
        rep(_,2){
          x *= ap.back();
          ap.pop_back();
        }
        v.push_back(x);
      }
      swap(ap,am);
    }
    sort(all(v), greater<ll>());

    rep(i,k/2){
      ans *= v[i];
    }

  }else{
    sort(all(a), [&](ll x, ll y){
      return abs(x) < abs(y);
    });

    rep(i,k){
      ans *= a[i];
    }
  }

  cout << ans.val() << endl;
 


  return 0;
}

// [*] を入れ忘れた場合の反例
// 5 5
// 1 2 3 -4 -5