Codeforces 166(#111 Div2 only) E. Buses and People
問題
n個の点(si, fi, ti)が与えられるので次のQ個のクエリに答えよ。
クエリ:点(x, y, z)に対して、最初のn個の点の中から、s≦x, f≧y, t≧zを満たす点のうち、tがもっとも小さいものの番号を答える。
ただし、最初のn点のtiは全て異なる。
制約条件
n, Q≦10^5
si, fi, ti≦10^9
方針
tiを座標圧縮する。
クエリと点をx座標の小さい順にソート。(x座標が同じなら、点を先に)
segment木のrange max queryを作っておいて、
点(f, t)がきたら、range max queryのtをfで更新する。
クエリ(y, z)が来たら、range max queryの[z, inf)の区間から、
値がy以上になっている最初の場所を二分探索で探せばよい。
この二分探索は、segment木を上から見ていくだけなので、O(log n)でできる。
(bitの二分探索みたいな感じ)
けど、制限時間がゆるいのでquery(L, R)を狭めていくO(log^2n)の二分探索でも通るっぽい。
ソースコード
const int MX = 100010; int n, m, ans[MX]; const int N = 131072; int dat[2 * N - 1]; inline void update(int k, int x){ k += N - 1; dat[k] = max(dat[k], x); while(k){ k = (k - 1) / 2; dat[k] = max(dat[k * 2 + 1], dat[k * 2 + 2]); } } inline int find(int a, int b, int l, int r, int k, int x){ if(b <= l || a >= r) return inf; if(dat[k] < x) return inf; if(l + 1 == r) return l; int t = find(a, b, l, (l + r) / 2, k * 2 + 1, x); if(t < inf) return t; return find(a, b, (l + r) / 2, r, k * 2 + 2, x); } int main(){ rep(i, 2 * N - 1) dat[i] = -inf; scanf("%d%d", &n, &m); vector<pair<pi, pi> > v; vi ts; map<int, int> id; rep(i, n){ int a, b, c; scanf("%d%d%d", &a, &b, &c); v.pb(mp(mp(a, i), mp(b, c))); id[c] = i + 1; ts.pb(c); } rep(i, m){ int a, b, c; scanf("%d%d%d", &a, &b, &c); v.pb(mp(mp(a, i + n), mp(b, c))); } sort(all(v)); sort(all(ts)); ts.erase(unique(all(ts)), ts.end()); rep(i, v.size()){ int c = lower_bound(all(ts), v[i].S.S) - ts.begin(); if(v[i].F.S < n){ update(c, v[i].S.F); } else{ int k = find(c, N, 0, N, 0, v[i].S.F); ans[v[i].F.S - n] = k < inf ? id[ts[k]] : -1; } } rep(i, m) printf("%d%c", ans[i], i == m - 1 ? '\n' : ' '); return 0; }