AtCoderのARC
次のARCの問題へ
前のARCの問題へ
ARC116-B Products of Min-Max
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("3");
WillReturn.Add("2 4 3");
//63
}
else if (InputPattern == "Input2") {
WillReturn.Add("1");
WillReturn.Add("10");
//100
}
else if (InputPattern == "Input3") {
WillReturn.Add("7");
WillReturn.Add("853983 14095 543053 143209 4324 524361 45154");
//206521341
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
const long Hou = 998244353;
static void Main()
{
List<string> InputList = GetInputList();
long[] AArr = InputList[1].Split(' ').Select(pX => long.Parse(pX)).ToArray();
Array.Sort(AArr);
int UB = AArr.GetUpperBound(0);
// Minを固定した時に、右側の掛ける数の合計
long RightSum = 0;
long Base2 = 1;
for (int I = 0; I <= UB; I++) {
if (I >= 2) {
Base2 *= 2;
Base2 %= Hou;
RightSum += AArr[I] * Base2;
}
else {
RightSum += AArr[I];
}
RightSum %= Hou;
}
long Answer = 0;
for (int I = 0; I <= UB; I++) {
if (I > 0) {
// 2で割る数以外を引いておく
RightSum -= AArr[I - 1];
RightSum -= AArr[I];
if (RightSum < 0) RightSum += Hou;
RightSum %= Hou;
// 2で割る(逆元を掛ける)
RightSum *= DeriveGyakugen(2);
RightSum %= Hou;
// カレントの項を足す
RightSum += AArr[I];
RightSum %= Hou;
}
//Console.WriteLine("左が{0}での掛ける数={1}", AArr[I], RightSum);
Answer += AArr[I] * RightSum;
Answer %= Hou;
}
Console.WriteLine(Answer);
}
//引数の逆元を求める
static long DeriveGyakugen(long pLong)
{
return DeriveModPow(pLong, Hou - 2, Hou);
}
//繰り返し2乗法で、(NのP乗) Mod Mを求める
static long DeriveModPow(long pN, long pP, long pM)
{
long CurrJyousuu = pN % pM;
long CurrShisuu = 1;
long WillReturn = 1;
while (true) {
//対象ビットが立っている場合
if ((pP & CurrShisuu) > 0) {
WillReturn = (WillReturn * CurrJyousuu) % pM;
}
CurrShisuu *= 2;
if (CurrShisuu > pP) return WillReturn;
CurrJyousuu = (CurrJyousuu * CurrJyousuu) % pM;
}
}
}
解説
まず全ての部分列が対象になるので
ソートしても問題ないのでソートします。
10 20 30 40 50 60 70 80
という数列で考え、
区間の最初を固定して考えることにします。
10が区間の最初の場合の総和は
10 * (10 + 20 + 30*2 + 40*4 + 50*8 + 60*16 + 70*32 + 80*64)
20が区間の最初の場合の総和は
20 * (20 + 30 + 40*2 + 50*4 + 60*8 + 70*16 + 80*32)
30が区間の最初の場合の総和は
30 * (30 + 40 + 50*2 + 60*4 + 70*8 + 80*16)
以上により
最初に、掛ける数を計算しておいて
掛ける数を引き算や2での割り算を減らしながら
総和を集計していけばいいと分かります。