Maximizing the Profit | HourRank 27
素朴に考えると
dp[i][j] := i個の要素を使って、前の部品のprofit factorがjのときの最大profit
のようなDPを得る。 例えば、サンプルの入力で6, 8を使うと dp[2][8] = 6*8 のようになる。
この方法には問題が3つあるが、どれも対処できる。
(1) profit factorの幅が大きい
座標圧縮すればよい。
(2) 絶対値の大きな負の数をうまく扱えない
1, 1, -10, -10, 10
のような数列を考えれば {-10, -10, 10} のような構成になるが、これには1ではなく-10を使う必要がある。(-10<1)。 そこで、以下のようなDPを考える。
ma[i][j] := i個の要素を使って、前の部品のprofit factorがjのときの最大profit mi[i][j] := i個の要素を使って、前の部品のprofit factorがjのときの最小profit
これで互いに値を見るように計算すればよい。
(3) DPの更新に時間がかかりすぎる
値p[k]を2番目の要素として計算することを考える。p[k]の圧縮後の座標をp'[k]とする。 ma[2][p'[k]] を更新するには ma[1][p'[k]-1], ma[1][p'[k]-2], ... , ma[1][0] と
mi[1][p'[k]-1], mi[1][p'[k]-2], ..., mi[1][0] を見る必要がある。
ほしい値は前者の最大値と後者の最小値なので、値の更新が速いRMQでDPすればよい。
実行時間
O(Nlg(N))
const ll MA = (ll)2e18; const ll MI = -(ll)2e18; int N; vi P; // セグメント木を使ったRMQ RMaxQ64 ma[4]; RMinQ64 mi[4]; int main() { ios::sync_with_stdio(false); cin.tie(0); cout << fixed << setprecision(20); cin >> N; P.resize(N); rep(i, N)cin >> P[i]; auto PP = compress(P); int M = sz(PP); rep(i, 4)ma[i] = RMaxQ64(M + 1, MI); rep(i, 4)mi[i] = RMinQ64(M + 1, MA); ma[0].update(0, 1); mi[0].update(0, 1); rep(i, N) { auto p = P[i]; ll val = PP[p]; for (int j = 2; j >= 0; --j) { ll x = ma[j].query(0, p + 1); ll y = mi[j].query(0, p + 1); if (x == MI)continue; x *= val; y *= val; if (x > y)swap(x, y); // x<=y if (x < mi[j + 1].query(p + 1, p + 2)) { mi[j + 1].update(p + 1, x); } if (y > ma[j + 1].query(p + 1, p + 2)) { ma[j + 1].update(p + 1, y); } } } ll ans = ma[3].query(0, M + 1); if (ans == MI)ans = -1; cout << ans << endl; }