bzoj 1584: [Usaco2009 Mar]Cleaning Up 打扫卫生
1584: [Usaco2009 Mar]Cleaning Up 打扫卫生
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
——————题解——————
确实是一道好题。
这题的思路很独特,我yy了半天也只想出了一个n^2的算法,还是太弱了。。
没想到这题的复杂度竟然是n*sqrt(n)。。。
我们假如把序列分为n段,答案显然是n。
我们进行DP是显然不能选一段不同的数>sqrt(n)的,因为一旦选了就肯定达不到最优。
这里还要维护一个量b[j]表示b[j]+1~i有j个不同的数,那么f[i]=min{f[b[j]]+j*j}。
但是怎么搞呢??
我们再定义pre数组,pre[a[i]]为a[i]上一次出现的位置,还有一个c[j]表示b[j]+1~i有c[j]个数不同
在i每次+1的时候,若pre[a[i]]<=b[j],则c[j]++,因为b[j]+1~i这一段没有包含a[i]
那么当前b[j]有变化的肯定是c[j]>j的那些。
然后暴力修改,不知道效率怎么样,但应该还是很快的
最后有个小优化,就是把像1 1 1 1 1这种改为1,对答案没有影响。
#include<stdio.h> #include<iostream> using namespace std; const int Max=2000000000; const int N=40005; int n,m,i,j,k,x,a[N],b[N],c[N],pre[N],f[N]; int main() { scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&x); if(x!=a[k]) a[++k]=x; } for(i=1;i<=k;i++) f[i]=Max; for(i=1;i<=k;i++) { for(j=1;j*j<=k;j++) if(pre[a[i]]<=b[j]) c[j]++; pre[a[i]]=i; for(j=1;j*j<=k;j++) if(c[j]>j) { x=b[j]+1; while(pre[a[x]]>x) x++; b[j]=x;c[j]--; } for(j=1;j*j<=k;j++) f[i]=min(f[i],f[b[j]]+j*j); } cout<<f[k]; return 0; }
一念起,天涯咫尺; 一念灭,咫尺天涯。