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
1
2
1
3
2
2
3
4
3
4
3
1
4
Sample Output
11
题解
不会做T T看了半天题解
这题首先可以发现最差情况是将数列a分成n段,代价为n
那么一定不能有划分出的一段超过√n个
这样一来每次的转移就是在√n个内了f[i]=min{f[b[j]]+j*j},j<=√n
其中b[j]表示的是从b[j]+1开始到i,共有j个不同的数字
对于b数组的维护,可以随意脑补一下
比如用pre[a[i]]记录a[i]上次出现的位置,c[j]记录b[j]+1到i的不同数字个数
这样每次i++时,先更新c数组,即如果pre[a[i]]<=b[i],那就无视,不然c[j]++
如果c[j]++了,那么就要从b[i]+1开始找一个只在b[j]+1到i出现一次的,删到它为止
复杂度好像应该是n√n了吧
对于所有,下限至少是n是可以确定的,因为每个单独一段,即可。
还有这个m是没有什么用的,那么我可以想如果一段的不同个数已经大于√n
那么其花费已经为n是没有意义的,但是这里的√n,是向下取整的所以还是
有意义的,可以取到
所以只需要记录不同的√n的转移即可,那么如何维护,
是根号n维护对吧,然后也是根号n转移即可,很好想的。
1 #include<cstring> 2 #include<cmath> 3 #include<iostream> 4 #include<algorithm> 5 #include<cstdio> 6 7 #define N 207 8 #define M 40007 9 using namespace std; 10 inline int read() 11 { 12 int x=0,f=1;char ch=getchar(); 13 while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} 15 return x*f; 16 } 17 18 int n,m; 19 int a[M],f[M]; 20 int b[N],cnt[N]; 21 int pre[M]; 22 23 int main() 24 { 25 memset(f,127/3,sizeof(f)); 26 memset(pre,-1,sizeof(pre)); 27 n=read();m=read(); 28 int tot=0; 29 for(int i=1;i<=n;i++) 30 { 31 int x=read(); 32 if(x!=a[tot])a[++tot]=x; 33 } 34 n=tot; 35 m=trunc(sqrt(n)); 36 f[0]=0; 37 for(int i=1;i<=n;i++) 38 { 39 for(int j=1;j<=m;j++) 40 if(pre[a[i]]<=b[j])cnt[j]++; 41 pre[a[i]]=i; 42 for(int j=1;j<=m;j++) 43 if(cnt[j]>j) 44 { 45 int t=b[j]+1; 46 while(pre[a[t]]>t)t++; 47 b[j]=t;cnt[j]--; 48 } 49 for(int j=1;j<=m;j++) 50 f[i]=min(f[i],f[b[j]]+j*j); 51 } 52 printf("%d",f[n]); 53 }