Official
E - Packing Under Range Regulations Editorial by physics0523
箱の番号の昇順に入れるボールを決定していく、以下のようなシミュレーションを考えます。
- 全ての \(1\) 以上 \(10^9\) 以下の整数 \(i\) に対して、以下の操作を行う。
- (A) \(L_j=i\) である全ての整数 \(j\) について、ボール \(j\) を「箱に入れてもいいボールのリスト」に入れる。
- (B)「箱に入れてもいいボールのリスト」から、ボールをどれか \(1\) つ決め、これを \(k\) とする。ボール \(k\) を箱 \(i\) に入れ、リストからボール \(k\) を削除する。
- (C) 「箱に入れてもいいボールのリスト」に \(R_j \le i\) なるボールが残っていれば、ボールを全て入れることはできない。
- \(i=10^9\) まで操作を行い、全てのボールが箱に入っていれば、それが条件を満たすボールの入れ方である。
では、このシミュレーションの (B) の部分は、どう決めれば最適でしょうか?
実は、リストに残っているボールのうち \(R_j\) が最小であるボール \(j\) を箱に入れると必ず最適にボールを箱に入れることができます。なぜなら、 \(R_j\) が最小でないボールを箱に入れた時と比べて、その後の (C) の部分の条件が厳しくなることはないからです。
「 \(R_j\) が最小であるボールを \(1\) つ選んで取り出す」という操作は、 priority_queue などを使うと実現可能です。また、この問題では実際に箱への入れ方を求める必要はないので、ボールをリストに追加する際に \(R_j\) の値だけを管理すればよいです。
また、冒頭のシミュレーションを愚直に行うと TLE してしまいますが、「リストが空になったタイミングで、次に (A) の操作によってリストが空でなくなるタイミングまで \(i\) を飛ばす」という改善によってこれを免れることが可能です。これには set を用いる、もしくは予めソートされた \(L\) の配列を二分探索する という方法があります。
以上より、この問題を時間計算量 \(O(N \log N)\) 程度で解くことが出来ました。
実装例(C++)
#include<bits/stdc++.h>
#define inf 1000000007
using namespace std;
using pi=pair<int,int>;
int main(){
int t;
cin >> t;
while(t>0){
t--;
int n;
cin >> n;
vector<pi> seg(n);
map<int,vector<int>> g;
set<int> st;
for(int i=0;i<n;i++){
cin >> seg[i].first >> seg[i].second;
g[seg[i].first].push_back(seg[i].second);
st.insert(seg[i].first);
}
st.insert(inf);
int i=(*st.begin());
priority_queue<int,vector<int>,greater<int>> pq;
bool fl=true;
while(i<=1000000000){
for(auto &nx : g[i]){pq.push(nx);}
int od=pq.top();
pq.pop();
if(pq.empty()){i=(*st.lower_bound(i+1));}
else{
if(pq.top()<=i){fl=false;break;}
i++;
}
}
if(!pq.empty()){fl=false;}
if(fl){cout << "Yes\n";}else{cout << "No\n";}
}
return 0;
}
posted:
last update: