POJ 2796 - Feel Good
http://poj.org/problem?id=2796
概要
n 個の emotional value の数列が与えられる. ある区間の emotional value は,その区間の emotional value の和と,その区間の最小値の積とする.
emotional value が最大になる区間と,そのときの値を答える. 最大になる区間が複数ある場合,どれを出力しても構わない.
制約
- 1 <= n <= 100000
- 0 <= emotional value <= 10^6
解法
emotional value は,区間の最小値が同じなら,できるだけ区間は広くしたほうがいいという点に注目する.
まず累積和と RMQ を作っておく. RMQ の実装は Sparce Table algorithm を使った. これは構築に O(N log N),クエリに O(1) という実装.
最初は [1, n] という区間から始めて,区間の emotional value を累積和から計算して最大値を更新する. そして,区間の最小値のインデックスで区間を左右に分け,再帰的に最大値を更新していく.
例えばサンプルに対しては
- 3 1 6 4 5 2
- 3
- 6 4 5 2
- 6 4 5
- 6
- 5
というように処理が進む.
はじめ,この処理を再帰関数で実装していたので,昇順・降順な数列について n 段スタックを消費して Runtime Error になっていた. そこで queue を使ったループに書き直したら 1907MS で通った. 何ページか status を眺めてみるとかなり遅いほうで,無理矢理通した感がある (そしてコードも長い…).
poj/2796.cc1 #include <cstdio> 2 #include <utility> 3 #include <queue> 4 using namespace std; 5 static const int M = 100000; 6 static const int K = 25; 7 8 int log2(int n) 9 { 10 int c = 0; 11 for (int x = 1; x <= n; x <<= 1) { 12 ++c; 13 } 14 return c-1; 15 } 16 17 struct RMQ 18 { 19 const int *v; 20 int (*m)[K]; 21 int N; 22 RMQ(const int *v_, int N_, int (*mem)[K]) 23 : v(v_), m(mem), N(N_) 24 { 25 for (int i = 0; i < N; i++) { 26 m[i][0] = i; 27 } 28 for (int j = 1; (1<<j) <= N; j++) { 29 for (int i = 0; i + (1<<j) - 1 < N; i++) { 30 if (v[m[i][j-1]] < v[m[i+(1<<(j-1))][j-1]]) { 31 m[i][j] = m[i][j-1]; 32 } else { 33 m[i][j] = m[i + (1<<(j-1))][j-1]; 34 } 35 } 36 } 37 } 38 39 int query(int i, int j) const 40 { 41 const int k = log2(j - i + 1); 42 if (v[m[i][k]] <= v[m[j-(1<<k)+1][k]]) { 43 return m[i][k]; 44 } else { 45 return m[j-(1<<k)+1][k]; 46 } 47 } 48 }; 49 50 pair<long long, pair<int,int> > solve(const int *v, const RMQ& m, const long long *sums, int beg, int end) 51 { 52 queue<pair<int,int> > q; 53 pair<long long, pair<int,int> > ans = make_pair((sums[end+1] - sums[beg]) * v[m.query(beg, end)], make_pair(beg, end)); 54 q.push(make_pair(beg, end)); 55 while (!q.empty()) { 56 const int b = q.front().first; 57 const int e = q.front().second; 58 const int i = m.query(b, e); 59 const long long x = (sums[e+1] - sums[b]) * v[i]; 60 q.pop(); 61 if (x > ans.first) { 62 ans.first = x; 63 ans.second = make_pair(b, e); 64 } 65 if (b <= i-1) { 66 q.push(make_pair(b, i-1)); 67 } 68 if (i+1 <= e) { 69 q.push(make_pair(i+1, e)); 70 } 71 } 72 return ans; 73 } 74 75 int main() 76 { 77 int N; 78 scanf("%d", &N); 79 static int v[M]; 80 for (int i = 0; i < N; i++) { 81 scanf("%d", &v[i]); 82 } 83 84 static int mem[M][K]; 85 RMQ rmq(v, N, mem); 86 87 static long long sums[M+1]; 88 for (int i = 0; i < N; i++) { 89 sums[i+1] = sums[i] + v[i]; 90 } 91 92 const pair<long long, pair<int,int> > ans = solve(v, rmq, sums, 0, N-1); 93 printf("%lld\n", ans.first); 94 printf("%d %d\n", ans.second.first+1, ans.second.second+1); 95 return 0; 96 }