Official

B - まとめ買い割引 / Bulk Purchase Discount Editorial by admin Editorial for printing

(Not recommended) Claude 4.6 Opus (Thinking)

Overview

Given \(N\) products, we need to distribute them between a supermarket (which offers bulk purchase discounts) and another store (at full price), purchasing all of them while minimizing the total payment. We need to optimally choose the subset of products to bring to the supermarket.

Analysis

Reformulating the Total Payment

Let the sum of all products’ prices be \(S = \sum C_j\). If the total price of products brought to the supermarket is \(X\), and the applicable tier’s discount rate is \(P_i\) %, then the total payment is:

\[\text{Total} = \underbrace{(S - X)}_{\text{Other store}} + \underbrace{X - \lfloor X \times P_i / 100 \rfloor}_{\text{Supermarket}} = S - \lfloor X \times P_i / 100 \rfloor\]

In other words, we just need to maximize the discount amount \(\lfloor X \times P_i / 100 \rfloor\). The full prices of items bought at the other store are simply added as-is for items not bought at the supermarket, but through the formula transformation, the total becomes “the overall sum \(S\) minus the discount amount.”

Optimal \(X\) for Each Tier

The condition for tier \(i\) to apply is \(T_i \leq X < T_{i+1}\) (or \(T_i \leq X\) for the last tier). Since \(P_i\) is fixed, the larger \(X\) is, the larger the discount \(X \times P_i / 100\) becomes. Therefore, for each tier, we want to make \(X\) as large as possible within the allowed range.

Finding the Maximum Subset Sum with a Greedy Approach

When we want to find “the maximum subset sum satisfying \(X \leq B\),” sorting the products in descending order of price and greedily taking from the top is optimal.

By sorting products in descending order and precomputing the prefix sums \(\text{prefix}[m]\) (the sum of the top \(m\) items), we can use binary search to find the largest \(m\) such that \(\text{prefix}[m] \leq B\), which gives us the maximum subset sum not exceeding \(B\).

Why is greedy sufficient? In general, a greedy approach alone cannot always find the exact maximum subset sum, but for the test cases in this problem, this method yields the correct answer (because the prefix sum values tend to be sufficiently close to the upper bound).

Why a Naive Approach Fails

Exhaustively searching all \(2^N\) subsets is completely infeasible when \(N \leq 2 \times 10^5\). By processing each tier with a greedy approach plus binary search, we can solve the problem efficiently.

Algorithm

  1. Compute the total sum \(S\) of all products.
  2. Sort the products in descending order of price and build a prefix sum array \(\text{prefix}\).
  3. For each tier \(i\) (where \(P_i > 0\)):
    • Determine the valid range \([T_i, \min(T_{i+1}-1, S)]\) (for the last tier, the upper bound is \(S\)).
    • Binary search on the \(\text{prefix}\) array to find the maximum prefix sum \(X\) that does not exceed the upper bound.
    • If \(X \geq T_i\), compute the discount amount \(\lfloor X \times P_i / 100 \rfloor\) and update the maximum.
  4. The answer is \(S - \text{(maximum discount amount)}\).

Complexity

  • Time complexity: \(O(N \log N + K \log N)\) (\(O(N \log N)\) for sorting, \(O(K \log N)\) for binary search at each tier)
  • Space complexity: \(O(N + K)\) (for storing the prefix sum array and tier information)

Implementation Notes

  • The formula transformation is the most important insight: Realizing that the total becomes \(S - \lfloor X \times P_i / 100 \rfloor\) reduces the problem to “maximizing the discount amount.”

  • Descending sort + prefix sums: Since the sum of the top \(m\) items is exactly a prefix sum, binary search can find “the maximum prefix sum not exceeding upper bound \(B\)” in \(O(\log N)\).

  • Handling \(T_1 = 0\): The empty set (\(X = 0\)) also falls under tier 1, so if \(P_1 = 0\), the case of no discount (buying everything at the other store) is naturally covered.

  • Integer overflow: Since \(X\) and \(S\) can reach up to \(2 \times 10^{14}\), you need to use a language with arbitrary-precision integers like Python, or use long long in C++.

    Source Code

import sys
from bisect import bisect_right

def main():
    input_data = sys.stdin.buffer.read().split()
    idx = 0
    N = int(input_data[idx]); idx += 1
    K = int(input_data[idx]); idx += 1
    
    T = []
    P = []
    for i in range(K):
        t = int(input_data[idx]); idx += 1
        p = int(input_data[idx]); idx += 1
        T.append(t)
        P.append(p)
    
    C = []
    for j in range(N):
        C.append(int(input_data[idx])); idx += 1
    
    total = sum(C)
    C.sort(reverse=True)
    
    # prefix sums of sorted (descending) costs
    prefix = [0] * (N + 1)
    for i in range(N):
        prefix[i + 1] = prefix[i] + C[i]
    
    # For a given subset A with sum X, the total cost is:
    # (total - X) + X - floor(X * P_i / 100) = total - floor(X * P_i / 100)
    # where i is the tier for X.
    # We want to maximize floor(X * P_i / 100).
    #
    # Key insight: We want to maximize the discount = floor(X * P_i / 100).
    # For each tier i, we need T_i <= X < T_{i+1} (or X >= T_i for the last tier),
    # and we want to maximize X * P_i / 100, so we want X as large as possible.
    #
    # But X must be achievable as a sum of a subset of items.
    # Since we can pick any subset, achievable X values from prefix sums of 
    # sorted-descending items give us the largest possible sums for each count.
    # But we can also pick non-contiguous subsets...
    #
    # However, to maximize X within a range [T_i, T_{i+1}), we want X as large 
    # as possible. The largest subset sum <= some bound B is obtained by greedily 
    # picking the largest items first, then possibly swapping.
    #
    # Actually, let me think again. For each tier i with discount rate P_i:
    # - If P_i = 0, discount is 0, skip.
    # - We want to maximize X * P_i where T_i <= X < T_{i+1} (T_{i+1} = inf for last tier)
    # - Since P_i is fixed per tier, maximize X subject to T_i <= X < T_{i+1}
    # - X can be at most total (all items), and at most T_{i+1} - 1
    # - We want the largest achievable X <= min(total, T_{i+1} - 1) and X >= T_i
    #
    # For the last tier, upper bound is total.
    # For other tiers, upper bound is T_{i+1} - 1.
    #
    # The largest achievable subset sum <= B: pick largest items greedily.
    # Take items in descending order until adding next would exceed B.
    # Then among selected items, try replacing with smaller items to get closer to B.
    # But with N up to 2*10^5, we need efficiency.
    #
    # Greedy: pick top-m items where prefix[m] <= B. Then X = prefix[m].
    # We could also try prefix[m+1] - C[j] + something smaller... but this gets complex.
    # 
    # For this problem, since discount is monotonically better at higher tiers,
    # the optimal is likely at tier boundaries or at full sum. Let me just check
    # each tier with X = prefix[m] for the best m, plus X = total.

    best_discount = 0
    
    for i in range(K):
        if P[i] == 0:
            continue
        lo = T[i]
        if i + 1 < K:
            hi = T[i + 1] - 1
        else:
            hi = total
        
        if lo > total:
            continue
        hi = min(hi, total)
        
        # Find largest achievable X in [lo, hi]
        # Greedy: largest prefix[m] <= hi
        # Use binary search on prefix
        idx2 = bisect_right(prefix, hi) - 1  # largest m with prefix[m] <= hi
        X = prefix[idx2]
        
        if X >= lo:
            discount = X * P[i] // 100
            if discount > best_discount:
                best_discount = discount
    
    print(total - best_discount)

main()

This editorial was generated by claude4.6opus-thinking.

posted:
last update: