[bzoj1587] [Usaco2009 Mar]Cleaning Up 打扫卫生

  首先(看题解)可得。。。分成的任意一段中的不同颜色个数都<=根号n。。。不然的话直接分成n段会更优= =

  然后就好做多了。。

  先预处理出对于每头牛i,和它颜色相同的前一头和后一头牛的位置。

  假设当前枚举到第i头牛,pos[j]表示 第pos[j]头~第i头牛之间颜色种数<=j。num[j]表示第pos[j]头~第i头牛之间具体的颜色种数。

  每次i增大时,就要把pos数组和num数组更新。对于新加进来的牛i,如果和它颜色相同的前一头牛不在pos[j]~i的范围中,那么就要把num[j]+1。

  但根据定义,num[j]<=j,所以如果num[j]>j的话就要把pos[j]往i的方向挪。。。如果与第pos[j]头牛颜色相同的后一头牛不在pos[j]~i的范围中,删去第pos[j]头牛后区间里的颜色数量就会-1。。。。

  DP,设f[i]表示将1~i头牛分成若干段的最小不和谐度。。f[i]=min{f[pos[j]-1]+num[j]^2},(1<=j<=n^0.5)。。。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cmath>
 5 #define us unsigned short
 6 using namespace std;
 7 const int maxn=40233;
 8 us pre[maxn],next[maxn],precol[maxn],nextcol[maxn];
 9 us a[maxn],b[707],c[707],f[maxn];//b[j]: b[j]~i中不同数字个数<=j c[j]表示b[j]+1到i出现的不同数字的个数
10 us i,j,n,m;
11 us ra;char rx;
12 inline us read(){
13     rx=getchar();ra=0;
14     while(rx<'0'||rx>'9')rx=getchar();
15     while(rx>='0'&&rx<='9')ra*=10,ra+=rx-48,rx=getchar();return ra;
16 }
17 int main(){
18     n=read();m=read();
19     for(i=1;i<=m;i++)nextcol[i]=n+1;m=floor(sqrt(n));
20     for(i=1;i<=n;i++)
21         a[i]=read(),pre[i]=precol[a[i]],precol[a[i]]=i;
22     for(i=n;i;i--)
23         next[i]=nextcol[a[i]],nextcol[a[i]]=i;
24     for(i=1;i<=m;i++)b[i]=1;
25     for(i=1;i<=n;i++)
26         for(j=1;j<=m;j++){
27             if(pre[i]<b[j]){
28                 c[j]++;
29                 if(c[j]>j){while(next[b[j]]<=i)b[j]++;b[j]++;c[j]--;}
30             }
31             if(f[b[j]-1]+c[j]*c[j]<f[i]||!f[i])f[i]=f[b[j]-1]+c[j]*c[j];
32         }
33     printf("%d\n",f[n]);
34     return 0;
35 }
View Code

 

posted @ 2015-12-22 13:56  czllgzmzl  阅读(533)  评论(0编辑  收藏  举报