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

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

 

 
posted @ 2016-05-29 12:37  lwq12138  阅读(191)  评论(0编辑  收藏  举报