競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 308F

ABC308

問題の本質から攻めるのが王道. 商品の個数は重要ではなく,大事なのはクーポン. まずはクーポンを全探索する方針で考える. つまり,クーポンを 1枚固定して考える.

払う値段は, \begin{align} & \sum_{i \in N} (p_{i} - c_{i}d_{i} )\\ = & \sum_{i \in N} p_{i} - \sum_{i \in N} c_{i}d_{i} \end{align} となる.ここで,\(c_{j}\)はクーポンを使ったら\(1\), 使わなかったら\(0\). クーポンが存在しない商品に対しては,\(d_{j} = 0\) とする. つまり,商品ごとに分解して,クーポンを使うかの割り当てを考えれば良い.

(0) : クーポンを固定したとき,どれに使うのが良いか. 使えるのは,\(L\) 円以上の商品ならどれでも良い. 後のことを考えると,\(L\) 円以上の商品のうち,一番安いものに 使うほうが良い. 言い換えると,\(L\)円以上の商品のうち,より安いものに選びなおしても 条件を満たす. つまり貪欲法と同じ.

(1) : クーポンを固定したときの最前の一つは求まった. 次は,使うクーポンの選び方を考える. なるべく値引きが大きいクーポンを使うほうが得. つまり,\(D\) を大きい順にソートしておく.

実際には,(0), (1) の通りに貪欲法で実装すればAC.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n, m ;
  cin >> n >> m;
  multiset<ll> p;
  ll ans = 0;
  rep(i,n){
    ll x; cin >> x;
    p.insert(x);
    ans += x;
  }
  vll l(m); cinv(l);
  vll d(m); cinv(d);

  vector<pll> v(m);
  rep(i,m){
    v[i] = {d[i], l[i]};
  }
  sort(all(v), greater<pll>());

  for(auto [d0, l0]: v){
    auto it = p.lower_bound(l0);

    if(it != p.end()){
      p.erase(it);
      ans -= d0;
    }
  }
  cout << ans << endl;  
 

  return 0;
}