1. 程式人生 > >CodeForces - 868F Yet Another Minimization Problem

CodeForces - 868F Yet Another Minimization Problem

|| inf -- efi min bits get 移動 個數

Description

給定一個長度為 \(n(n\le 10^5)\) 的數列,第 \(i\) 個數是 \(a_i\in[1,n]\) ,要求將其劃分為 \(k(2\le k\le min(20,n))\) 段以後每段價值和最小。

定義一段的價值為該段相同數的數對個數。

Solution

定義 \(calc(l,r)\)\([l,r]\) 這一段的價值, \(dp[j][i]\) 為前 \(i\) 個數劃分為 \(j\) 段的最小價值。那麽顯然有

\[ dp[j][i]=min\{dp[j-1][i']+calc(i'+1,i),i'<i\} \]

因為 \(dp[j][i]\)

僅由 \(dp[j-1][i']\) 轉移來,不妨設 \(j\) 固定。

\(f(i)\) 表示 \(i\) 對應的最優決策點 \(i'\)打表不難發現 \(f(i)\) 單調遞增。

考慮分治。\(solve(l,r,L,R)\) 表示對於 \(i\in [l,r]\) ,有 \(f(i)\in {L,R}\) 。枚舉出 \(mid=\cfrac{l+r}{2}\) 的決策點 \(f(mid)\) 後分治 \(solve(l,mid-1,L,f(mid))\)\(solve(mid+1,r,f(mid),R)\) 即可。

對於 \(calc(l,r)\) ,因為 \(l\)

\(r\) 的每次移動後都可以 \(O(1)\) 計算答案,所以可以莫隊。

#include<bits/stdc++.h>
using namespace std;

template <class T> inline void read(T &x) {
    x = 0; static char ch = getchar(); for (; ch < '0' || ch > '9'; ch = getchar());
    for (; ch >= '0' && ch <= '9'
; ch = getchar()) (x *= 10) += ch - '0'; } #define N 100001 #define rep(i, a, b) for (int i = a; i <= b; i++) #define ll long long const ll INF = 0x3f3f3f3f3f3f3f3f, P = 1e9 + 7; int n, K, a[N], now, nxt = 1, lf = 1, ri, cnt[N]; ll dp[2][N], sum; inline void calc(int l, int r) { while (ri < r) sum += cnt[a[++ri]], cnt[a[ri]]++; while (ri > r) cnt[a[ri]]--, sum -= cnt[a[ri--]]; while (lf > l) sum += cnt[a[--lf]], cnt[a[lf]]++; while (lf < l) cnt[a[lf]]--, sum -= cnt[a[lf++]]; } #define mid (l + r >> 1) void solve(int l, int r, int L, int R) { if (l > r) return; dp[nxt][mid] = INF; int t; rep(i, L, min(mid, R)) { calc(i, mid); if (dp[nxt][mid] > dp[now][i - 1] + sum) dp[nxt][mid] = dp[now][i - 1] + sum, t = i; } solve(l, mid - 1, L, t), solve(mid + 1, r, t, R); } int main() { read(n), read(K); rep(i, 1, n) read(a[i]); memset(dp, INF, sizeof dp); dp[now][0] = 0; while (K--) solve(1, n, 1, n), swap(now, nxt); printf("%lld", dp[now][n]); return 0; }

CodeForces - 868F Yet Another Minimization Problem