[BZOJ1584][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

 


 

 

这题真的...打死我都想不到可以用这种方法优化...

首先设$f[i]$表示$i$之前的最优答案, 那么$f[i] = min(f[j]+dif^2)$。

这是$O(N^2)$的显然会T。

我们发现答案的最差值一定是$n$(每一个都当做一组),所以我们用来转移的区间只有不同颜色数量在$[1, \sqrt{N}]$之间。

然后就可以$O(N \sqrt{N})$转移了。

不太会写抄的网上的题解。

根号算法无处不在。

 

 


 

 

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
#include <algorithm>
using namespace std;
#define reg register 
#define gc getchar
inline int read() {
    int res=0;char ch=gc();bool fu=0;
    while(!isdigit(ch)){if(ch=='-')fu=1;ch=gc();}
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=gc();
    return fu?-res:res;
}
#define N 40005
int n, m;
int a[N], pre[N], nxt[N], pos[N], lst[N], cnt[N];
int f[N];

int main() 
{
    n = read(), m = read();
    for (reg int i = 1 ; i <= n ; i ++) a[i] = read(), pre[i] = -1, nxt[i] = n + 1;
    for (reg int i = 1 ; i <= n ; i ++) {
        if (lst[a[i]]) pre[i] = lst[a[i]];
        lst[a[i]] = i;
    }
    memset(lst, 0, sizeof lst);
    for (reg int i = n ; i >= 1 ; i --) {
        if (lst[a[i]]) nxt[i] = lst[a[i]];
        lst[a[i]] = i;
    }
    memset(f, 0x3f, sizeof f);
    f[0] = 0;
    for (reg int i = 1 ; i <= n ; i ++) 
    {
        for (reg int j = 1 ; j * j <= n ; j ++) 
            if (pre[i] <= pos[j]) cnt[j]++;
        for (reg int j = 1 ; j * j <= n ; j ++)
            if (cnt[j] > j) {
                pos[j] ++;
                while(nxt[pos[j]] <= i) pos[j]++;
                cnt[j]--;
            }
        for (reg int j = 1 ; j * j <= n ; j ++) f[i] = min(f[i], f[pos[j]] + j * j);
    }
    cout << f[n] << endl;
    return 0;
}

 

posted @ 2018-10-15 13:55  zZhBr  阅读(272)  评论(0编辑  收藏  举报