問題概要
a[] = { a1, a2, a3, ... , an } の増加部分列が与えられる。a[i,j]の区間の最頻値が何度現れるかというクエリQ個(Q ≦ 105個)に答えよ。
解法
見た感じセグ木で解けそうな問題。それぞれの数字の出現回数を求めて、ノードに最大値と合計の情報を持たせたセグ木に入れる。
合計の情報は累積和でやっても同じ。
クエリに対しては、合計の情報より二分探索で左端と右端を求めてRangeMaximumQuery。
左端と右端は微妙に被るので、例外処理する。
これで計算量はO((さんきゅー + N) log N) くらいになる。
ソース
#include<bits/stdc++.h> using namespace std; typedef pair< int, int > Pi; struct SegmentTree { int sz; vector< int > data, big; SegmentTree(int n) { sz = 1; while(sz < n) sz *= 2; data.assign(2 * sz + 1, 0); big.assign(2 * sz + 1, 0); } Pi Lower_Bound_Lower(int k, int x) { // 上の節目からどれだけ離れているか if(k >= sz - 1) return(make_pair(k - sz + 1, data[k] - x)); if(data[2 * k + 1] >= x) return(Lower_Bound_Lower(2 * k + 1, x)); return(Lower_Bound_Lower(2 * k + 2, x - data[2 * k + 1])); } int RangeMaximumQuery(int a, int b, int k, int l, int r) { if(a >= r || b <= l) return(0); if(a <= l && r <= b) return(big[k]); return(max(RangeMaximumQuery(a, b, 2 * k + 1, l, (l + r) >> 1), RangeMaximumQuery(a, b, 2 * k + 2, (l + r) >> 1, r))); } int Query(int a, int b) { // [a,b) Pi Left = Lower_Bound_Lower(0, a), Right = Lower_Bound_Lower(0, b); int l = data[sz - 1 + Left.first] - Left.second - 1, r = Right.second; if(Left.first == Right.first) return(data[sz - 1 + Left.first] - r - l); int hasiL = data[sz - 1 + Left.first] - l; int hasiR = data[sz - 1 + Right.first] - r; return(max(max(hasiL, hasiR),RangeMaximumQuery(Left.first + 1, Right.first, 0, 0, sz))); } void Update(int k, int x) { k += sz - 1; data[k] += x; big[k] = data[k]; while(k > 0) { k = (k - 1) >> 1; data[k] = data[2 * k + 1] + data[2 * k + 2]; big[k] = max(big[2 * k + 1], big[2 * k + 2]); } } }; int main() { int n, q, digit[100000]; while(scanf("%d", &n), n){ SegmentTree seg(200002); scanf("%d", &q); for(int i = 0; i < n; i++) { scanf("%d", &digit[i]); digit[i] += 100000; } int cnt = 1; for(int i = 1; i < n; i++) { if(digit[i - 1] != digit[i]) { seg.Update(digit[i - 1], cnt); cnt = 1; } else { ++cnt; } } seg.Update(digit[n - 1], cnt); for(int i = 0; i < q; i++) { int a, b; scanf("%d %d", &a, &b); printf("%d\n", seg.Query(a, b)); } } }
0 件のコメント:
コメントを投稿