C - Counting 2 Editorial by blackyuki
各クエリについて愚直に答えを計算すると、計算量 \(O(QN)\) で実行時間超過(TLE)してしまいます。
各クエリについて高速に答えを計算する方法を考えます。
前計算として、\(N\) 人を身長の昇順にソートしておきます。
昇順にソートされた長さ \(N\) の数列 \(A\) と整数 \(x\) が与えられた時、二分探索を用いると計算量 \(O(\log N)\) で \(x \leq A_i\) を満たす最小の \(i\) を求めることができます。以下は二分探索の概要です。
求めたい \(i\) が区間 \([l,r]\) にあることが分かっているとする(初期状態では \(l = 0, r = N\) など)。
\(md =\left\lfloor \dfrac{l + r}{2}\right\rfloor\) として、\(x \leq A_{md}\) のとき求めたい \(i\) は区間 \([l,md]\) にあり、そうでない場合区間 \([md+1,r]\) にあることが分かる。
\(1\) 回のステップで区間の長さが半分以下になるから、全体で \(O(\log N)\) のステップ数で区間の長さが \(1\) となり、求めたい \(i\) が求められる。
なお、多くの言語では二分探索の標準ライブラリが提供されているので、それを用いると簡潔に実装することができます。
実装例1 (C++)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, q; cin >> n >> q;
vector<int> v(n);
for(int i = 0; i < n; i++) cin >> v[i];
sort(v.begin(),v.end());
for(int i = 0; i < q; i++){
int x; cin >> x;
int ok = n, ng = -1; // v[ok] >= x, v[ng] < x であることが分かっている
while(ok - ng > 1){
int md = (ok + ng) / 2;
if(v[md] >= x) ok = md;
else ng = md;
}
cout << n - ok << endl;
}
}
実装例2 (C++)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, q; cin >> n >> q;
vector<int> v(n);
for(int i = 0; i < n; i++) cin >> v[i];
sort(v.begin(),v.end());
for(int i = 0; i < q; i++){
int x; cin >> x;
cout << v.end() - lower_bound(v.begin(), v.end(), x) << endl;
}
}
posted:
last update: