解题思路
我们可以设吃掉第i盘菜的最少时间为
f
i
f_i
fi
首先我们要明确,这个最少时间的总数一定不超过
n
n
n(一个一个吃都最大为n),设
j
j
j为一次吃
j
j
j盘菜,那么我们显然可知j最大为
s
q
r
t
(
n
)
sqrt(n)
sqrt(n)
那么我们构思一个O(nsqrt(n))的算法:
用
p
i
,
j
pi,j
pi,j表示从第i盘菜往前数,得到
j
j
j个菜种的最远离第
i
i
i盘菜的菜的位置,然后由于每次都需要更新这个p值(因为加了第i盘菜嘛)所以我们把
p
i
,
j
pi,j
pi,j简化为
p
j
pj
pj
得方程:
f
i
=
m
i
n
f
[
p
[
j
]
−
1
]
+
j
2
fi=min{f[p[j]-1] +j^2}
fi=minf[p[j]−1]+j2
那么我们要怎么维护p?我们设一个 l a s t k lastk lastk表示第k种编号的菜系最迟出现在什么地方,然后分两种情况:
- l a s t [ d i s h i ] last[dishi] last[dishi]< p j pj pj(表示 p j pj pj~ i i i 里没 d i s h i dishi dishi 这盘菜), p j = p j − 1 pj=pj-1 pj=pj−1(因为将要多一个菜)
- l a s t [ d i s h i ] last[dishi] last[dishi]>= p j pj pj(表示 p j pj pj~ i i i 里有 d i s h i dishi dishi 这盘菜), p j = p j pj=pj pj=pj
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll long long
#define ldb long double
using namespace std;
int n,m,k,l,a,f[40010],p[40010],last[40010];
int main() {
freopen("cleanup.in","r",stdin);
freopen("cleanup.out","w",stdout);
memset(last,-1,sizeof(last));
scanf("%d%d",&n,&m);
scanf("%d",&a);
l=sqrt(n);
f[1]=1;
p[1]=1;
last[a]=1;
for(int i=2; i<=n; i++) {
scanf("%d",&a);
p[0]=i;
for(int j=l;j>0;j--)
if(last[a]<p[j])
p[j]=p[j-1];
f[i]=i;
for(int j=1;j<=l;j++)
if(p[j]-1>0)
f[i]=min(f[i],f[p[j]-1]+j*j);
last[a]=i;
}
printf("%d",f[n]);
}