从前往后DP;
先离散化;
假设DP到第i个位置。
las[i]表示第i种颜色最后一次出现的位置。
t[k]表示满足w(t[i],i)==k的最小下标,w(a,b)表示从a,a+1,a+2......b这段区间的不同颜色的数量是多少。
然后每次先更新t数组,再更新dp数组,k只需从1枚举到sqrt(n),所以复杂度为(n*sqrt(n)).
数组更新的时候,有两种情况。
1.第i个位置的颜色前边没出现过,则全部t[i]都要更新,表示为 for(int j=up;j>=1;j--)t[j+1]=t[j],t[1]=i,up++;
2.第i个位置的颜色前边出现过,则需更新部分t[i],只需更新t[i]>las[a[i]]的那些t[i].
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <map> #define inf 0x3f3f3f3f using namespace std; const int maxn=50005; map<int,int>mp; int cnt,n; int las[maxn],dp[maxn],a[maxn],t[250];//las[i]表示最后一次i出现的位置,dp[i]表示到前i个的最小花费。 int ID(int x){ if(!mp.count(x))mp[x]=++cnt; return mp[x]; } void run(){ mp.clear(); cnt=0; for(int i=1;i<=n;i++){ scanf("%d",a+i); a[i]=ID(a[i]); } memset(las,0,sizeof las); int up=1; t[1]=1; dp[1]=1; las[a[1]]=1; for(int i=2;i<=n;i++){ dp[i]=inf; if(!las[a[i]]){ for(int j=up;j>=1;j--)t[j+1]=t[j]; if(up*up<=n)up++; t[1]=i; } else { int m=up; while(m&&t[m]<=las[a[i]])m--; for(int j=m-1;j>=1;j--)t[j+1]=t[j]; if(m)t[1]=i; } las[a[i]]=i; for(int j=1;j*j<=i;j++){ dp[i]=min(dp[i],dp[t[j]-1]+j*j); } } printf("%d ",dp[n]); } int main() { // freopen("in","r",stdin); while(scanf("%d",&n)>0)run(); return 0; }