ARC037 C問題 億マス計算
昨日の ABC155 の D 問題の類題であり、D 問題はこれを引っ張ってきて何とか通せた。
実装を無限にバグらせて、通ったのコンテスト終了 10 分前くらいだけど…
問題概要
長さ の数列 が与えられる。
の計算で現れた 個の値を昇順で並べたときの 番目にある値は何か。
制約
(考察 →) 実装
基本問題っぽいけど、よく分からないとなってた気がする。
まず、愚直に計算してソートは、 となって間に合わない。
そこで、答えとなる を決め打ちする。
二分探索でよくある (?) 答えの決め打ちである。
ここで、 が 番目の値になり得る条件というのは、 以下の値が 個以上あることである、と言い換えることができる。
これを二分探索時の判定条件にする。
次に、個数をどう数えるかだが、積が 以下であるというのは、
つまり
と書くことができる。
この個数も二分探索で求めることができる。
二分探索のために、数列 をあらかじめ昇順でソートしておく。
を見ているとき、 が 以下である個数は、 より大きくなる最初の位置 (イテレータ) を調べれば、求めることができる。
(そのイテレータを it とすると、it - b.begin() で個数は求まる。)
手順をまとめると、
- 数列 を昇順にソートする。
- を (ng, ok] で二分探索する。 このとき、数列 の要素を順に見ていき、数列 の要素との積が 以下となる個数を数え上げる。 この個数を と比較して、範囲を狭めていく。
これで、計算量は、ソート部分が 、二分探索部分が となり解くことができる。
コード
横着して上限部分を にしている。
#include <iostream> #include <vector> #include <algorithm> #include <cstdint> using namespace std; int64_t N, K; vector<int64_t> a, b; // X 以下の数が K 個以上か否か // K 個以上あれば、(ng, ok] → (ng, mid] // K 個以上なければ、(ng, ok] → (mid, ok] bool check(int64_t X){ int64_t cnt = 0; for(int i = 0; i < N; ++i){ // a_i * b_j が X 以下になる個数を足していく cnt += upper_bound(b.begin(), b.end(), X / a[i]) - b.begin(); } return cnt >= K; } int main(){ cin >> N >> K; a.resize(N); b.resize(N); for(int i = 0; i < N; ++i) cin >> a[i]; for(int i = 0; i < N; ++i) cin >> b[i]; sort(b.begin(), b.end()); // (ng, ok] で二分探索 int64_t ng = 0, ok = 1e+18; while(ok - ng > 1){ int64_t mid = (ng + ok) / 2; if(check(mid)) ok = mid; else ng = mid; } cout << ok << endl; }
ABC155 の D 問題は、答えが、負、、正のうちどの場合かを判定し、 以外の場合に上述のような操作をしていけば解ける。
それに手こずりすぎていたわけだが…
入水はギリギリならず… (レート : 1197)
Codeforces は無事に水色に戻った。