#include <iostream>
#include <random>
#include <algorithm>
#include <numeric>
#include <vector>
#include <chrono>
#include <cstdlib>
#include <iomanip>
#include <iterator>
#include <functional>
enum
{
kDPLimit = 5000000000, // 5*10^9, dynamic programming
kHSLimit = 54, // Horowitz-Sahni
kKKLimit = 4, // Karmarkar-Karp
};
using namespace std;
using i64 = int_fast64_t;
using u64 = uint_fast64_t;
class Log
{
public:
Log(size_t size, i64 max_value, i64 sum, u64 seed, ostream& os)
: os_(os)
, start_(chrono::steady_clock::now())
{
os_ << "size=" << size
<< " max=" << static_cast<double>(max_value)
<< " sum=" << sum
<< " seed=" << seed
<< '\n';
}
template <typename M>
void operator() (M msg)
{
const chrono::duration<double> d = chrono::steady_clock::now() - start_;
os_ << d.count() << "s: " << msg << '\n';
}
void name(const char* name)
{
os_ << name << '\n';
}
private:
ostream& os_;
const chrono::time_point<chrono::steady_clock> start_;
};
template<typename I>
void dp(I b, I e, i64 sum, Log& log)
{
log.name("dynamic programming");
vector<char> subset_sums(sum / 2 + 1, false);
subset_sums[0] = true;
for_each(b, e, [&](i64 value)
{
for (i64 i = sum / 2 - value; i >= 0; --i)
{
if (subset_sums[i])
subset_sums[i + value] = true;
}
});
const auto it = find(subset_sums.rbegin(), subset_sums.rend(), true);
const auto pos = subset_sums.rend() - it - 1;
log(sum - 2 * pos);
}
void mergeValue(vector<i64>& to, const vector<i64>& from, i64 value)
{
to.clear();
auto original = begin(from);
auto summed = begin(from);
const auto last = end(from);
while (original != last)
{
if (summed == last)
{
copy(original, last, back_inserter(to));
return;
}
if (*summed + value < *original)
{
to.push_back(*summed + value);
++summed;
}
else
{
to.push_back(*original);
++original;
}
}
transform(summed, last, back_inserter(to),
[value](i64 x){return x + value;});
}
template<typename I>
vector<i64> subsetSums(I b, I e)
{
auto isize = e - b;
const auto size = 1 << isize;
vector<i64> subset_sums;
subset_sums.reserve(size);
vector<i64> subset_sums2;
subset_sums2.reserve(size / 2);
vector<i64>* const ways[2] {&subset_sums, &subset_sums2};
ways[isize&1]->push_back(0);
while (isize--)
mergeValue(*ways[isize&1], *ways[(isize+1)&1], *(b + isize));
return subset_sums;
}
template<typename I>
void hs(I b, I e, i64 sum, Log& log)
{
log.name("Horowitz-Sahni");
const auto middle = (e - b) / 2;
const vector<i64> first_half = subsetSums(b, b + middle);
const vector<i64> second_half = subsetSums(b + middle, e);
const auto target = sum / 2;
auto result = sum;
auto it1 = begin(first_half);
auto it2 = second_half.rbegin();
while (it1 != end(first_half) && it2 != second_half.rend())
{
const auto both = *it1 + *it2;
if (both > target)
{
result = min(result, 2 * both - sum);
++it2;
}
else
{
result = min(result, sum - 2 * both);
++it1;
}
if (result <= 1)
break;
}
log(result);
}
i64 kkMH(const vector<i64>& values, Log& log)
{
log.name("Karmarkar-Karp max heap");
vector<i64> pq(values);
make_heap(begin(pq), end(pq));
while (pq.size() > 1)
{
auto first = pq[0];
pop_heap(begin(pq), end(pq));
pq.pop_back();
auto second = pq[0];
pop_heap(begin(pq), end(pq));
pq[pq.size() - 1] = first - second;
push_heap(begin(pq), end(pq));
}
log(pq[0]);
return pq[0];
}
template<bool Preprocess = false>
i64 kk(const vector<i64>& values, i64 sum, Log& log)
{
log.name("Karmarkar-Karp");
vector<i64> pq(values.size() * 2);
copy(begin(values), end(values), begin(pq) + values.size());
sort(begin(pq) + values.size(), end(pq));
auto first = end(pq);
auto last = begin(pq) + values.size();
while (first - last > 1)
{
if (Preprocess && first - last <= kHSLimit)
{
hs(last, first, sum, log);
return 0;
}
if (Preprocess && static_cast<double>(first - last) * sum <= kDPLimit)
{
dp(last, first, sum, log);
return 0;
}
const auto diff = *(first - 1) - *(first - 2);
sum -= *(first - 2) * 2;
first -= 2;
//const auto place = find_if(last, first, [diff](i64 x){return x>=diff;});
const auto place = lower_bound(last, first, diff);
--last;
copy(last + 1, place, last);
*(place - 1) = diff;
}
const auto result = (first - last)? *last: 0;
log(result);
return result;
}
// arguments: size, max value, random seed (integers, 0x or 0 prefix allowed)
int main(int argc, char **argv)
{
size_t size = 10000;
i64 max_value = 100000000000000ll; // 10^14
u64 seed = random_device{}();
if (argc > 1)
size = min<u64>(size, strtoull(argv[1], nullptr, 0));
if (argc > 2)
max_value = min<u64>(max_value, strtoull(argv[2], nullptr, 0));
if (argc > 3)
seed = strtoull(argv[3], nullptr, 0);
mt19937_64 rng(seed);
uniform_int_distribution<i64> ud(1, max_value);
auto gen = bind(ud, rng);
vector<i64> values(size);
generate_n(begin(values), size, gen);
const auto sum = accumulate(begin(values), end(values), 0ll);
Log log(size, max_value, sum, seed, cout);
if (kk(values, sum, log) > 1 && size > kKKLimit)
kk<true>(values, sum, log);
}