제출 #1254698

#제출 시각아이디문제언어결과실행 시간메모리
1254698fasterthanyou축제 (IOI25_festival)C++20
컴파일 에러
0 ms0 KiB
#include <vector> #include <algorithm> #include <cstdint> #include <utility> using std::vector; // Minimal non-negative BigInt (base 1e9) sufficient for this task. struct BigInt { static constexpr uint32_t BASE = 1000000000u; vector<uint32_t> d; // little-endian digits BigInt() = default; BigInt(uint64_t v) { assign(v); } void assign(uint64_t v) { d.clear(); if (v == 0) return; while (v > 0) { d.push_back(static_cast<uint32_t>(v % BASE)); v /= BASE; } } bool is_zero() const { return d.empty(); } void trim() { while (!d.empty() && d.back() == 0) d.pop_back(); } int cmp(const BigInt& o) const { if (d.size() != o.d.size()) return d.size() < o.d.size() ? -1 : 1; for (int i = static_cast<int>(d.size()) - 1; i >= 0; --i) { if (d[i] != o.d[i]) return d[i] < o.d[i] ? -1 : 1; } return 0; } int cmp_small(uint64_t x) const { if (d.empty()) return x == 0 ? 0 : -1; if (d.size() >= 3) return 1; // >= BASE^2 > 1e18 > any 64-bit x we will pass here uint64_t val = (d.size() == 2) ? static_cast<uint64_t>(d[0]) + static_cast<uint64_t>(d[1]) * BASE : static_cast<uint64_t>(d[0]); if (val < x) return -1; if (val > x) return 1; return 0; } void add(const BigInt& y) { size_t n = std::max(d.size(), y.d.size()); d.resize(n, 0); uint64_t carry = 0; for (size_t i = 0; i < n; ++i) { uint64_t sum = carry + d[i] + (i < y.d.size() ? y.d[i] : 0u); d[i] = static_cast<uint32_t>(sum % BASE); carry = sum / BASE; } if (carry) d.push_back(static_cast<uint32_t>(carry)); } void add_small(uint64_t x) { uint64_t carry = x; size_t i = 0; while (carry > 0) { if (i == d.size()) d.push_back(0); uint64_t sum = static_cast<uint64_t>(d[i]) + carry; d[i] = static_cast<uint32_t>(sum % BASE); carry = sum / BASE; ++i; } } // assume this >= y void sub(const BigInt& y) { uint64_t borrow = 0; for (size_t i = 0; i < d.size(); ++i) { uint64_t yi = (i < y.d.size() ? y.d[i] : 0u); uint64_t cur = static_cast<uint64_t>(d[i]); uint64_t sub_v = yi + borrow; if (cur >= sub_v) { d[i] = static_cast<uint32_t>(cur - sub_v); borrow = 0; } else { d[i] = static_cast<uint32_t>(cur + BASE - sub_v); borrow = 1; } } trim(); } // assume this >= x void sub_small(uint64_t x) { uint64_t carry = x; size_t i = 0; while (carry > 0) { uint64_t cur = (i < d.size() ? d[i] : 0u); uint64_t xi = carry % BASE; carry /= BASE; if (cur >= xi) { d[i] = static_cast<uint32_t>(cur - xi); } else { d[i] = static_cast<uint32_t>(cur + BASE - xi); carry += 1; // borrow } ++i; } trim(); } void mul_small(uint32_t m) { if (is_zero() || m == 1) return; if (m == 0) { d.clear(); return; } uint64_t carry = 0; for (size_t i = 0; i < d.size(); ++i) { uint64_t prod = static_cast<uint64_t>(d[i]) * m + carry; d[i] = static_cast<uint32_t>(prod % BASE); carry = prod / BASE; } if (carry) d.push_back(static_cast<uint32_t>(carry)); } }; static inline bool operator< (const BigInt& a, const BigInt& b) { return a.cmp(b) < 0; } static inline bool operator<=(const BigInt& a, const BigInt& b) { return a.cmp(b) <= 0; } static inline bool operator> (const BigInt& a, const BigInt& b) { return a.cmp(b) > 0; } static inline bool operator>=(const BigInt& a, const BigInt& b) { return a.cmp(b) >= 0; } static inline bool operator==(const BigInt& a, const BigInt& b) { return a.cmp(b) == 0; } static inline bool operator!=(const BigInt& a, const BigInt& b) { return a.cmp(b) != 0; } vector<int> max_coupons(int A, const vector<int>& P, const vector<int>& T) { struct Item { int idx; int price; int mult; // T >= 2 int64_t k_num; // P * T int k_den; // T - 1 }; const int n = static_cast<int>(P.size()); vector<Item> boosters; vector<std::pair<int,int>> plains; // (price, idx) for T==1 boosters.reserve(n); plains.reserve(n); for (int i = 0; i < n; ++i) { if (T[i] == 1) { plains.emplace_back(P[i], i); } else { boosters.push_back(Item{ i, P[i], T[i], static_cast<int64_t>(P[i]) * static_cast<int64_t>(T[i]), T[i] - 1 }); } } // Comparator for K = P*T/(T-1), exact via cross-multiplication. auto keyLessItem = [&](const Item& a, const Item& b) -> bool { int64_t left = a.k_num * b.k_den; int64_t right = b.k_num * a.k_den; if (left != right) return left < right; if (a.price != b.price) return a.price < b.price; return a.idx < b.idx; }; // Coordinate-compress booster prices vector<int> uniquePrices; uniquePrices.reserve(boosters.size()); for (auto &it : boosters) uniquePrices.push_back(it.price); std::sort(uniquePrices.begin(), uniquePrices.end()); uniquePrices.erase(std::unique(uniquePrices.begin(), uniquePrices.end()), uniquePrices.end()); const int M = static_cast<int>(uniquePrices.size()); auto priceToCoord = [&](int price) -> int { return static_cast<int>(std::lower_bound(uniquePrices.begin(), uniquePrices.end(), price) - uniquePrices.begin()); }; // Group boosters by price coordinate and sort each group by K vector<vector<int>> groups(M); // holds indices into boosters for (int i = 0; i < static_cast<int>(boosters.size()); ++i) { int d = priceToCoord(boosters[i].price); groups[d].push_back(i); } for (int d = 0; d < M; ++d) { std::sort(groups[d].begin(), groups[d].end(), [&](int i, int j){ return keyLessItem(boosters[i], boosters[j]); }); } vector<int> ptr(M, 0); // next candidate in each price group // Segment tree over price coords, each node stores best item index by K struct SegTree { int n; vector<int> t; // index into boosters, or -1 const vector<vector<int>>* groups; vector<int>* ptr; const vector<Item>* items; SegTree(int n_, const vector<vector<int>>* groups_, vector<int>* ptr_, const vector<Item>* items_) : n(n_), t(n_ ? 4*n_ : 0, -1), groups(groups_), ptr(ptr_), items(items_) {} int pick(int a, int b) const { if (a == -1) return b; if (b == -1) return a; const Item& ia = (*items)[a]; const Item& ib = (*items)[b]; int64_t left = ia.k_num * ib.k_den; int64_t right = ib.k_num * ia.k_den; if (left != right) return (left < right) ? a : b; if (ia.price != ib.price) return (ia.price < ib.price) ? a : b; return (ia.idx < ib.idx) ? a : b; } void build(int v, int tl, int tr) { if (tl == tr) { int d = tl; int candidate = -1; if ((*ptr)[d] < static_cast<int>((*groups)[d].size())) { candidate = (*groups)[d][(*ptr)[d]]; } t[v] = candidate; } else { int tm = (tl + tr) >> 1; build(v<<1, tl, tm); build(v<<1|1, tm+1, tr); t[v] = pick(t[v<<1], t[v<<1|1]); } } void build() { if (n > 0) build(1, 0, n-1); } void update(int v, int tl, int tr, int pos) { if (tl == tr) { int d = tl; int candidate = -1; if ((*ptr)[d] < static_cast<int>((*groups)[d].size())) { candidate = (*groups)[d][(*ptr)[d]]; } t[v] = candidate; } else { int tm = (tl + tr) >> 1; if (pos <= tm) update(v<<1, tl, tm, pos); else update(v<<1|1, tm+1, tr, pos); t[v] = pick(t[v<<1], t[v<<1|1]); } } void update(int pos) { if (n > 0) update(1, 0, n-1, pos); } int query(int v, int tl, int tr, int l, int r) const { if (l > r) return -1; if (l == tl && r == tr) return t[v]; int tm = (tl + tr) >> 1; int left = query(v<<1, tl, tm, l, std::min(r, tm)); int right = query(v<<1|1, tm+1, tr, std::max(l, tm+1), r); return pick(left, right); } int query(int l, int r) const { if (n == 0 || l > r) return -1; return query(1, 0, n-1, l, r); } }; SegTree st(M, &groups, &ptr, &boosters); st.build(); auto lastAffordableCoord = [&](const BigInt& tokens) -> int { int lo = 0, hi = M; // find first price > tokens, then step back while (lo < hi) { int mid = (lo + hi) >> 1; if (tokens.cmp_small(static_cast<uint64_t>(uniquePrices[mid])) >= 0) { lo = mid + 1; } else { hi = mid; } } return lo - 1; }; // Build greedy booster sequence BigInt tokens(static_cast<uint64_t>(A)); vector<BigInt> tokens_after; // tokens after buying k boosters tokens_after.push_back(tokens); vector<int> booster_seq; while (true) { int r = lastAffordableCoord(tokens); if (r < 0) break; int id = st.query(0, r); if (id == -1) break; const Item& it = boosters[id]; tokens.sub_small(static_cast<uint64_t>(it.price)); tokens.mul_small(static_cast<uint32_t>(it.mult)); booster_seq.push_back(it.idx); tokens_after.push_back(tokens); int d = priceToCoord(it.price); ++ptr[d]; st.update(d); } // Sort T=1 coupons by price asc, then idx std::sort(plains.begin(), plains.end()); int M1 = static_cast<int>(plains.size()); // Prefix sums of T=1 prices vector<BigInt> pref; pref.reserve(M1 + 1); pref.emplace_back(0u); for (auto &q : plains) { BigInt next = pref.back(); next.add_small(static_cast<uint64_t>(q.first)); pref.push_back(std::move(next)); } auto max_plains_with = [&](const BigInt& x) -> int { // largest m with pref[m] <= x int lo = 0, hi = M1; while (lo < hi) { int mid = (lo + hi + 1) >> 1; if (pref[mid] <= x) lo = mid; else hi = mid - 1; } return lo; }; // Choose best prefix of boosters (to maximize boosters + T=1) int best_total = -1; int best_k = 0; for (int k = 0; k < static_cast<int>(tokens_after.size()); ++k) { int m = max_plains_with(tokens_after[k]); int total = k + m; if (total > best_total) { best_total = total; best_k = k; } } // Build final order: first best_k boosters in greedy order, then as many T=1 as possible vector<int> order; order.reserve(best_total > 0 ? best_total : 0); for (int i = 0; i < best_k; ++i) order.push_back(booster_seq[i]); BigInt tok = tokens_after[best_k]; for (int i = 0; i < M1; ++i) { if (tok.cmp_small(static_cast<uint64_t>(plains[i].first)) >= 0) { tok.sub_small(static_cast<uint64_t>(plains[i].first)); order.push_back(plains[i].second); } else { break; } } return order; }

컴파일 시 표준 에러 (stderr) 메시지

/usr/bin/ld: /tmp/cckFRUu0.o: in function `main':
grader.cpp:(.text.startup+0x232): undefined reference to `max_coupons(int, std::vector<int, std::allocator<int> >, std::vector<int, std::allocator<int> >)'
collect2: error: ld returned 1 exit status