Acwing163 生日禮物
阿新 • • 發佈:2022-04-09
這題我的第一反應就是 WQS 二分,發現題解沒有類似的想法,於是記錄一下。
首先,考慮確定分為 \(m\) 段怎麼做,我們可以設 \(f[n][m][0/1]\) 表示前 \(n\) 個數,分為 \(m\) 段,當前這個數取不取,最大的子段和。
那麼顯然有轉移式:
容易分析該 \(dp\)
可是,本題要求的是不大於 \(m\) 的最優情況,同樣利用 \(dp\) 的上凸性,我們可以三分搜尋這個 \(m\),再套上 WQS 二分,即可求得答案。
時間複雜度 \(O(n\log m \log \sum |A_i|)\)
這個時間複雜度還是比較高的,在 Acwing 上提交需要套上八聚氧才能通過(放在部落格上就不加八聚氧了,太醜了)
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int maxn = 100005; typedef long long ll; int n, m; int a[maxn]; ll f[maxn][2], g[maxn][2]; pair<ll, ll> dp(ll k) { f[0][1] = -2e9; for (int i = 1; i <= n; i++) { int mx = max(f[i - 1][0], f[i - 1][1]); f[i][0] = mx; if (f[i - 1][0] == mx) g[i][0] = g[i - 1][0]; else g[i][0] = g[i - 1][1]; mx = max(f[i - 1][1] + a[i], max(f[i - 1][0] + a[i] - k, f[i - 1][1] + a[i] - k)); f[i][1] = mx; if (f[i - 1][0] + a[i] - k == mx) g[i][1] = g[i - 1][0] + 1; else if (f[i - 1][1] + a[i] - k == mx) g[i][1] = g[i - 1][1] + 1; else g[i][1] = g[i - 1][1]; } if (f[n][1] >= f[n][0]) return make_pair(f[n][1], g[n][1]); else return make_pair(f[n][0], g[n][0]); } ll solve(ll x) { ll lb = -1e9, rb = 1e9; while (lb < rb) { int mid = (lb + rb) >> 1; pair<ll, ll> tmp = dp(mid); if (tmp.second > x) lb = mid + 1; else if (tmp.second == x) { lb = mid; break; } else rb = mid; } return dp(lb).first + x * lb; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); int lb = 0, rb = m; while (rb - lb > 2) { int lmid = lb + (rb - lb) / 3, rmid = rb - (rb - lb) / 3; if (solve(lmid) > solve(rmid)) rb = rmid; else lb = lmid; } ll ans = 0; for (int i = lb; i <= rb; i++) ans = max(ans, solve(i)); printf("%lld\n", ans); return 0; }