---
# System prepended metadata

title: IOI2016 Day1-1 分子の検出 (Detecting Molecules)
tags: [IOI]

---

---
tags: IOI
---
# IOI2016 Day1-1 分子の検出 (Detecting Molecules)

簡単か？

## 問題文

https://www.ioi-jp.org/ioi/2016/tasks/day1-molecules-JPN.pdf
https://oj.uz/problem/view/IOI16_molecules

数列 $w$ と区間 $[l, u]$ が与えられます。 
ただし $u - l ≥ max(w) - min(w)$ です。
空でない $w$ の(連続のほうでない)部分列であって合計が $[l, u]$ に入っているものを1つ求めてください。

## 考察

$u - l ≥ max(w) - min(w)$ がかなり重要そう。
これの意味を考えると、合計が $u$ を超えた部分列の選ぶものを小さいものに変更しても $[l, u]$ を通り過ぎることはないことがわかる。
そこで、大きい方から選んで行って、合計が $u$ を超えたら選ぶものを小さいものに変えていけばよい。

## 実装

https://oj.uz/submission/210848
```cpp
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#include "molecules.h"
#define rep(a) for(ll i = 0; i < a; i++)
#define all(a) begin(a), end(a)
#define sum(a) accumulate(all(a), 0LL)
 
vector<int> find_subset(int l, int u, vector<int> w){
    ll n = w.size();
    vector<ll> a(n);
    iota(all(a), 0);
    sort(all(a), [&](ll a, ll b){ return w[a] < w[b]; });
    if(w[a[0]] > u) return {};
    if(l > sum(w)) return {};
    ll s = 0, at = n;
    vector<int> ans;
    while(s <= u){
        if(l <= s) return ans;
        ans.push_back(a[--at]);
        s += w[ans.back()];
    }
    n = min<ll>(ans.size(), n - ans.size());
    rep(n){
        s -= w[ans[i]];
        ans[i] = a[i];
        s += w[ans[i]];
        if(s <= u) return ans;
    }
    return {};
}
```