luogu P2943 [USACO09MAR]Cleaning Up G
题面传送门
显然可以\(dp\)
\(n^2\)暴力\(dp\)很好想,循环时直接更新答案即可。
考虑优化。
显然,如果每\(1\)头牛都分成\(1\)组,那么代价为\(n\),所以我们每次枚举状态时种类不能超过\(\sqrt n\),否则为无用转移。
那\(\sqrt n\)种类数之内的最左端点怎么搞呢?
显然可以尺取法。跑\(\sqrt n\)趟尺取即可。
时间复杂度\(O(n\sqrt n)\)
代码实现:
#include<cstdio>
#include<cmath>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,f[100039],dp[100039],l[439],tot[439],s[239][100039],a[100039];
int main(){
register int i,j;
scanf("%d%d",&n,&m);
k=sqrt(n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
for(i=1;i<=n;i++){
dp[i]=1e9;
for(j=1;j<=k;j++){
s[j][a[i]]++;
tot[j]+=(s[j][a[i]]==1?1:0);
while(tot[j]>j){
s[j][a[l[j]+1]]--;
tot[j]-=(s[j][a[l[j]+1]]==0?1:0);
l[j]++;
}
//printf("%d ",tot[j]);
dp[i]=min(dp[i],dp[l[j]]+j*j);
}
//printf("%d\n",dp[i]);
}
printf("%d\n",dp[n]);
}