#4718. 管理
题解
考虑暴力dp: $f_{i,j}$表示前 $i$ 个分 $j$ 段的最小值,于是 $f_{i,j}=min{f_{k,j-1}+w_{k+1,i}}$
其实想一下应该会发现上述dp具有决策单调性,于是我们可以分治求出每个 $i$ 的决策点
效率: $O(nklogn)$
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=1e5+5; int n,m,a[N],c[N],p[N];LL f[N][25]; void solve(int l,int r,int L,int R,int j,LL k){ if (L==R){ for (int i=l;i<=r;i++) p[i]=L;return; } int mid=(l+r)>>1;LL v=k; for (int i=l+1;i<=mid;i++) v+=c[a[i]],c[a[i]]++; LL w=v+f[L][j];p[mid]=L; for (int i=L+1;i<=min(R,mid-1);i++){ c[a[i]]--;v-=c[a[i]]; if (w>f[i][j]+v) w=f[i][j]+v,p[mid]=i; } for (int i=min(R,mid-1);i>p[mid];i--) v+=c[a[i]],c[a[i]]++; v+=c[a[mid+1]];c[a[mid+1]]++; if (mid<r) solve(mid+1,r,p[mid],R,j,v); for (int i=p[mid];i>L;i--) v+=c[a[i]],c[a[i]]++; for (int i=mid+1;i>l;i--) c[a[i]]--,v-=c[a[i]]; if (l<mid) solve(l,mid-1,L,p[mid],j,v); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ scanf("%d",&a[i]); f[i][1]=f[i-1][1]+c[a[i]]; c[a[i]]++; } for (int i=1;i<=n;i++) c[i]=0; for (int j=2;j<=m;j++){ c[a[j]]++;solve(j,n,j-1,n-1,j-1,0); c[a[j]]--;LL v=0; for (int i=j,l=j-1,r=j-2;i<=n;i++){ while(r<i) r++,v+=c[a[r]],c[a[r]]++; while(l<p[i]+1) c[a[l]]--,v-=c[a[l]],l++; f[i][j]=f[p[i]][j-1]+v; } for (int i=1;i<=n;i++) c[i]=0; } cout<<f[n][m]<<endl;return 0; }