AtCoderのABC
次のABCの問題へ
前のABCの問題へ
ABC173-E Multiplication 4
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("4 2");
WillReturn.Add("1 2 -3 -4");
//12
}
else if (InputPattern == "Input2") {
WillReturn.Add("4 3");
WillReturn.Add("-1 -2 -3 -4");
//1000000001
}
else if (InputPattern == "Input3") {
WillReturn.Add("2 1");
WillReturn.Add("-1 1000000000");
//1000000000
}
else if (InputPattern == "Input4") {
WillReturn.Add("10 10");
WillReturn.Add("1000000000 100000000 10000000 1000000 100000 10000 1000 100 10 1");
//999983200
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
const long Hou = 1000000007;
static long mK;
static long[] mAArr;
static long UB;
static void Main()
{
List<string> InputList = GetInputList();
long[] wkArr = InputList[0].Split(' ').Select(pX => long.Parse(pX)).ToArray();
mK = wkArr[1];
UB = mK;
mAArr = InputList[1].Split(' ').Select(pX => long.Parse(pX)).ToArray();
long Result = Solve();
Console.WriteLine(Result);
}
static long Solve()
{
// 正の数にできるかの判定
bool CanCreatePlus = DeriveCanCreatePlus();
// 正の数にできない場合
if (CanCreatePlus == false) {
return DeriveMaxNonPlus();
}
// 正の数にできる場合
return DeriveMaxPlus();
}
// 正の数にできるかの判定
static bool DeriveCanCreatePlus()
{
long RestK = mK;
long PlusCnt = 0;
long MinusCnt = 0;
foreach (long EachLong in mAArr) {
if (EachLong == 0) continue;
if (EachLong > 0) PlusCnt++;
if (EachLong < 0) MinusCnt++;
}
// 優先順位1 2個ペアで負数を使う
while (RestK >= 2 && MinusCnt >= 2) {
RestK -= 2;
MinusCnt -= 2;
}
if (RestK == 0) {
return true;
}
// 優先順位2 正の数を使う
while (RestK >= 1 && PlusCnt >= 1) {
RestK--;
PlusCnt--;
}
return RestK == 0;
}
// 正の数にできない場合の、最大値を返す
static long DeriveMaxNonPlus()
{
long WillReturn = 1;
// 絶対値の昇順でのK個の積
foreach (long EachLong in mAArr.OrderBy(pX => Math.Abs(pX)).Take((int)mK)) {
WillReturn *= EachLong;
WillReturn %= Hou;
}
if (WillReturn < 0) WillReturn += Hou;
return WillReturn;
}
// 正の数にできる場合の、最大値を返す
static long DeriveMaxPlus()
{
var PlusList = mAArr.Where(pX => pX > 0).OrderByDescending(pX => pX).ToList();
var MinusList = mAArr.Where(pX => pX < 0).OrderBy(pX => pX).ToList();
long WillReturn = 1;
long RestK = mK;
// 奇数の場合は、正の最大値を事前に取る
if (RestK % 2 == 1) {
WillReturn *= PlusList[0];
WillReturn %= Hou;
RestK--;
PlusList.RemoveAt(0);
}
if (RestK == 0) {
return WillReturn;
}
// 2個ペアを作る
var PlusPairList = new List<long>();
var MinusPairList = new List<long>();
for (int I = 1; I <= PlusList.Count - 1; I += 2) {
// Modを取らずに比較する必要あり
PlusPairList.Add(PlusList[I - 1] * PlusList[I]);
}
for (int I = 1; I <= MinusList.Count - 1; I += 2) {
// Modを取らずに比較する必要あり
MinusPairList.Add(MinusList[I - 1] * MinusList[I]);
}
var UnionList = new List<long>();
UnionList.AddRange(PlusPairList);
UnionList.AddRange(MinusPairList);
// 降順で2個ずつ取る
foreach (long EachLong in UnionList.OrderByDescending(pX => pX)) {
long Mod = EachLong % Hou;
WillReturn *= Mod;
WillReturn %= Hou;
RestK -= 2;
if (RestK == 0) break;
}
return WillReturn;
}
}
解説
最初に場合分けで、
正の数を作れる場合と
正の数を作れない場合に分けます。
正の数を作れるかの判定は、
優先順位1 マイナスの数が2つあったら使う
優先順位2 プラスの数を使う
これでK個の数を使えれば積を正にできます。
正の数を作れない場合は、絶対値の昇順にK個取るのが最適になります。
正の数を作れる場合は、
さらにKの偶奇で場合分けします。
Kが奇数の場合は、
負の数を偶数個、正の数を奇数個使う必要があるので、
最大の正の数を最初に使って、
負の数を偶数個、正の数を偶数個使う状態に遷移できます。
Kが偶数の場合は、
負の数を偶数個、正の数を偶数個使う必要があるので、
負の数の昇順での2個ずつペアの積と
正の数の降順での2個ずつペアの積を
混ぜた集合から、降順で K / 2 個の積が解になります。