AOJ 282 Programming Contest
方針としては、以下の2種類の操作がO( log(n) )程度で行えれば時間内に解けるだろと考えられる。
- i番目のチームの得点をx増やす
- 全チームの内, 得点が最大のチームの番号を求める
よってセグメント木でRange Minimum Queryを実装したら良さそうという発想に至る。
ただし、以下の様に少し工夫する必要がある。
- (当たり前だが) 最小値(Minimum)ではなく最大値を求める
- データとなる数列は, a[i] := pair(i番目のチームの得点, -i) とする
特に二つ目が重要で, これにより以下の効用が得られる。
- 最大値を求める際に, 最大値をとるチームの番号を同時に求めることができる
- pairには比較演算子が定義されているので, 問題で指示されている (チームの得点が高い順 => 得点が同じなら番号の若い順) での順序付けが楽に行える
セグメント木によるRMQの実装は蟻本のサンプルを拝借して改変。
#include <cstdio> #include <algorithm> #define INF 1000000000 #define MAX_N 1 << 18 using namespace std; typedef pair<int, int> P; int N, R, L; int dur[MAX_N]; int n; P score[2 * MAX_N - 1]; void init(int n_){ n = 1; while(n < n_) n *= 2; for(int i = 0; i < 2 * n - 1; i++) score[i] = P(-INF, -INF); } void update(int k_, int a){ int k = k_ + n - 1; score[k] = P(a, - k_); while(k > 0){ k = (k - 1) / 2; score[k] = max(score[k * 2 + 1], score[k * 2 + 2]); } } P query(int a, int b, int k, int l, int r){ if(r <= a || b <= l) return P(-INF, -INF); if(a <= l && r <= b) return score[k]; else{ P vl = query(a, b, k * 2 + 1, l, (l + r) / 2); P vr = query(a, b, k * 2 + 2, (l + r) / 2, r); return max(vl, vr); } } int top_id(){ return - query(0, N, 0, 0, n).second; } int add_score(int id, int x){ update(id, score[id + n - 1].first + x); } int main(){ scanf("%d %d %d", &N, &R, &L); init(N); for(int i = 0; i < N; i++) update(i, 0); int time = 0; for(int i = 0; i < R; i++){ int d, t, x; scanf("%d %d %d", &d, &t, &x); dur[top_id()] += t - time; time = t; add_score(d - 1, x); } dur[top_id()] += L - time; int ans, max_dur = -1; for(int i = 0; i < N; i++) if(max_dur < dur[i]){ max_dur = dur[i]; ans = i + 1; } printf("%d\n", ans); }