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