Solution -「洛谷 P4983」忘情
阿新 • • 發佈:2020-08-21
\(\mathcal{Description}\)
Link.
給定序列 \(\{a_n\}\) 和 \(m\),定義一段區間 \([l,r]\) 的代價為 \((1+\sum_{i=l}^ra_i)^2\),求把序列劃分為恰 \(m\) 段的最小代價和。
\(\mathcal{Solution}\)
忘情(水)二分 owo!
設 WQS 二分當前一段區間的附加代價 \(k\),考慮 DP,令 \(f(i)\) 為任意劃分 \([1,i]\) 的最小代價。記 \(s_i\) 為字首和,顯然:
\[\begin{aligned} f(i)&=\min_{j\in[0,i)}\{f(j)+(s_i-s_j+1)^2\}+k\\ &=k+(s_i+1)^2+\min_{j\in[0,i)}\{(f(j)+s_j^2-2s_j)-2s_is_j\} \end{aligned} \]
注意到斜優的影子,令 \(g(i)=f(i)+s_i^2-2s_i\),考慮 \(i\) 的兩個轉移點 \(v<u\),若 \(u\) 更優,則有:
\[g(u)-2s_is_u<g(v)-2s_is_v\\ \Rightarrow \frac{g(u)-g(v)}{s_u-s_v}<2s_i \]
於是在 WQS 裡面套一個斜優即可。複雜度 \(\mathcal O(n\log\sum_{i=1}^na_i)\)。
\(\mathcal{Code}\)
#include <cstdio> typedef long long LL; const int MAXN = 1e5; const LL INF = 1.1e16; int n, m, trc[MAXN + 5], que[MAXN + 5]; LL f[MAXN + 5], s[MAXN + 5]; inline int rint () { int x = 0; char s = getchar (); for ( ; s < '0' || '9' < s; s = getchar () ); for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' ); return x; } inline LL g ( const int i ) { return f[i] + s[i] * s[i] - 2 * s[i]; } inline double slope ( const int i, const int j ) { return 1.0 * ( g ( i ) - g ( j ) ) / ( s[i] - s[j] ); } inline bool check ( const LL mid, LL& res ) { for ( int head, tail, i = head = tail = 1; i <= n; ++ i ) { for ( ; head < tail && slope ( que[head], que[head + 1] ) < 2 * s[i]; ++ head ); trc[i] = trc[que[head]] + 1; f[i] = f[que[head]] + ( s[i] - s[que[head]] + 1 ) * ( s[i] - s[que[head]] + 1 ) + mid; for ( ; head < tail && slope ( que[tail - 1], que[tail] ) > slope ( que[tail], i ); -- tail ); que[++ tail] = i; } return trc[n] <= m ? res = f[n] - m * mid, false : true; } int main () { n = rint (), m = rint (); for ( int i = 1; i <= n; ++ i ) s[i] = s[i - 1] + rint (); LL l = -INF, r = INF, ans = 0; while ( l <= r ) { LL mid = l + r >> 1; if ( check ( mid, ans ) ) l = mid + 1; else r = mid - 1; } printf ( "%lld\n", ans ); return 0; }