bzoj1584
1584: [Usaco2009 Mar]Cleaning Up 打扫卫生
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 467 Solved: 316
[Submit][Status][Discuss]
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
HINT
Source
不愿意动脑子,也想不出来
首先我们可以发现,因为最小值最大也就是n,也就是把所有东西分成长度为1的段
所以我们可以知道绝对不可以让一段有>=n^0.5种数字
考虑dp,设b[j]为一段有j种数字,最近对应的位置(区间为i-b[j]+1),pre[i]:上一个数字i出现的位置,cnt[j]:其实记录更新时有没有修改。
方程就得出了:f[i]=min{f[b[j]]+j*j} 1<=j<=n^0.5 复杂度为O(n^1.5)
怎么更新b呢?可以发现,当一个新的数字被加进时,b[j]有可能修改,当且仅当从i-b[j]+1中没有这个数字,这时我们用cnt记录被修改,然后一个一个向前找,直到我们可以删掉一个数字,使得这段中有j个数字
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define N 40010 int n,m; int f[N],a[N],pre[N],b[N],cnt[N]; int main() { memset(f,0x3f,sizeof(f)); f[0]=0; memset(pre,-1,sizeof(pre)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } int size=(int)(sqrt(n)); for(int i=1;i<=n;i++) { for(int j=1;j<=size;j++) { if(pre[a[i]]<=b[j]) cnt[j]++; } pre[a[i]]=i; for(int j=1;j<=size;j++) { if(cnt[j]>j) { int pos=b[j]+1; while(pre[a[pos]]>pos) pos++; b[j]=pos; cnt[j]--; } } for(int j=1;j<=size;j++) { f[i]=min(f[i],f[b[j]]+j*j); } } printf("%d",f[n]); return 0; }