从前往后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\n",dp[n]);
}

int main()
{
//    freopen("in","r",stdin);
    while(scanf("%d",&n)>0)run();
    return 0;
}