P2943 [USACO09MAR]Cleaning Up G

洛谷传送门

Solution

这种题很显然能想到用区间DP去做。

那么我们先列一个最朴素的方程: \(f_{i}=\min(f_{i},f_{j}+s)\) ,其中 \(s\)\(i\) ~ \(j\) 的不同种类数。

这是 \(O(n^2)\) 的显然不太可。

那有什么能优化的?

发现如果将每只奶牛单独作为一段的话,最后的答案是 \(n\)

所以 \(s\) 中的种类数应该小于 \(\sqrt n\) ,不然代价就超过 \(n\) 了。

我们拿一个数组 \(pos\) ,表示段内有 \(i\) 个数,其中右端点为当前点,左端点为 \(pos_i\) ,那么现在的转移方程就是:

\[f_{i=}\min_{j=1}^{\sqrt n}\{f_{pos_j-1}+j^2,f_{i}\} \]

考虑如何维护 \(pos\) ԅ(¯﹃¯ԅ)

可以拿 \(\sqrt n\) 个桶,然后对当前位即 \(P_i\) 进行判断,如果是新的值,加入桶,判断从 \(pos_j\)\(i\) 是否是 \(j\) 个数,要是大于,就及时往右移,直到满足条件。

Code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
const int N=4e4+10,B=210;
int n,m,a[B][N],cnt[B],ans,f[N],P[N],pos[B];

inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}

int main(){
    n=read(); m=read();
    for(int i=1;i<=n;i++) P[i]=read();
    memset(f,0x3f3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<B;j++){
            ++a[j][P[i]];
            if(a[j][P[i]]==1){
                ++cnt[j];
                if(cnt[j]>j){
                    while(--a[j][P[pos[j]]]!=0) ++pos[j];
                    ++pos[j];
                    cnt[j]=j;
                }
            }
            if(cnt[j]==j) f[i]=min(f[i],f[pos[j]-1]+j*j);
        }
    printf("%d\n",f[n]);
    return 0;
}
posted @ 2020-11-04 19:54  jasony_sam  阅读(125)  评论(0编辑  收藏  举报