BZOJ_1584_[Usaco2009 Mar]Cleaning Up 打扫卫生_DP

BZOJ_1584_[Usaco2009 Mar]Cleaning Up 打扫卫生_DP

Description

有N头奶牛,每头那牛都有一个标号Pi,1 <= Pi <= M <= N <= 40000。现在Farmer John要把这些奶牛分成若干段,定义每段的不河蟹度为:若这段里有k个不同的数,那不河蟹度为k*k。那总的不河蟹度就是所有段的不河蟹度的总和。

Input

第一行:两个整数N,M

第2..N+1行:N个整数代表每个奶牛的编号

Output

一个整数,代表最小不河蟹度

Sample Input

13 4
1
2
1
3
2
2
3
4
3
4
3
1
4

Sample Output

11


 

设F[i]表示前i个数分成若干段。

首先能看出来不同的数最多只需要根号n个,再多就不如一个一个取了。

如果我们能求出来L[j]表示当前选j个不同的数最早能到哪,那么有F[i]=min(F[i],F[L[j]-1]+j*j)。

其中L[j]是和F[i]一起转移的。

那么我们维护根号n个指针求L即可。

 

代码:

#include <cstdio>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
#define N 40050
int n,m,a[N],L[250],H[250][40050],cnt[250],f[N];
int main() {
    scanf("%d%*d",&n); int i,j;
    for(m=sqrt(n),i=1;i<=n;i++) scanf("%d",&i[a]);
    for(i=1;i<=m;i++) L[i]=1;
    memset(f,0x3f,sizeof(f)); f[0]=0;
    for(i=1;i<=n;i++) {
        for(j=1;j<=m;j++) {
            H[j][a[i]]++; if(H[j][a[i]]==1) cnt[j]++;
            while(cnt[j]>j&&L[j]<=i) {
                H[j][a[L[j]]]--; if(H[j][a[L[j]]]==0) cnt[j]--; L[j]++;
            }
            f[i]=min(f[i],f[L[j]-1]+j*j);
        }
    }
    printf("%d\n",f[n]);
}

 

posted @ 2018-06-02 21:06  fcwww  阅读(167)  评论(0编辑  收藏  举报