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
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]); }