链接:https://codeforces.com/problemset/problem/868/F
首先n^2m的dp很容易想,f(j,i)表示考虑划分了j个集合,最后一个集合结尾在i的最小值。显然它具有决策单调性,即如果f(j,i)最后一个划分的集合在最优方案下起始位置是k,那么当i增加k一定单调不降(如果有多个最优决策位置选任意一个都是满足的,因为实际上i增大一定是在最右边的最优决策对它影响最小,所以最右的及它左边的都满足该性质),考虑用这个性质优化dp,假设当前j已经确定,要处理所有i在[l,r]内的答案,那么可以先求mid的答案,递归处理左右区间,可以发现左右区间因为之前那个性质可能是决策位置的总长度才等于当前区间的可能长度,所以如果能做到每一层求mid答案与可能决策区间长度同阶即可做到nm^log,而发现不与当前决策区间同阶的限制就是要算mid~可能决策区间右端点的数的贡献,而这个东西发现我们每次分治的时候只要不把整一层信息清空带着当前层的右端点mid到下一层移动右端点移动也是总和n^log了。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+100;
const ll inf=1e18;
template<class T>
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
int n,m,a[N],dep,nw_l,nw_r;
vector<int>bin;
ll cnt[N],f[22][N],res;
ll get(int x)
{return max(0LL,cnt[x]*(cnt[x]-1)/2);}
void add(int x)
{res-=get(a[x]),cnt[a[x]]++,res+=get(a[x]);}
void del(int x)
{res-=get(a[x]),cnt[a[x]]--,res+=get(a[x]);}
void sol(int l,int r,int may_l,int may_r)
{
ll mn=inf;
int ps,mid=(l+r)>>1;
while(nw_r<mid)add(++nw_r);;
while(nw_r>mid)del(nw_r--);
for(int i=min(may_r,mid);i>=may_l;i--)
{
while(nw_l>i)add(--nw_l);
while(nw_l<i)del(nw_l++);
if(res+f[dep-1][i-1]<=mn)ps=i,mn=res+f[dep-1][i-1];
}
f[dep][mid]=mn;
if(l<mid)sol(l,mid-1,may_l,ps);
if(r>mid)sol(mid+1,r,ps,may_r);
}
int main()
{
rd(n),rd(m);
for(int i=1;i<=n;i++)
rd(a[i]);
for(int i=1;i<=n;i++)
add(i),f[1][i]=res;
memset(cnt,0,sizeof cnt);
for(int j=2;j<=m;j++)
{
memset(cnt,0,sizeof cnt);
nw_l=1,nw_r=0,dep=j,res=0;
sol(1,n,1,n);
}
printf("%lld\n",f[m][n]);
}