que
Que
【Description】
给出一个n个数组成的序列,这n个数的值∈[1,m],对于一段区间,如果其中值的种类数为K,那么我们称这段区间的价值为K2。请将序列进行合适的划分,使得总价值最小。总价值为各段价值之和。
【Input Format】
第一行两个数n,m。
接下来n行,每行一个数,表示序列中每个数的值。
【Output Format】
一个数,表示总价值。
【Sample Input】
13 4
1
2
1
3
2
2
3
4
3
4
3
1
4
【Sample Output】
11
分析:这道题的解法其实挺巧妙的。
尤其是下面程序中的对q,p数组的维护部分,
采用动态更新的方法,
我也是单步执行了好久才看出一点端倪。。。
(注:q[i]表示从当前点开始向左种数为i的最长区间的左端点位置,
p[i]表示当前点前i这个值出现的最靠右位置,
这两个数组互相动态更新)
program que; var q:array[0..250]of longint; a,p,f:array[0..40005]of longint; tmp,tail,i,j,k,n,m,len:longint; vv:boolean; function min(x,y:longint):longint; begin if x<y then exit(x); exit(y); end; begin assign(input,'que.in'); reset(input); assign(output,'que.out'); rewrite(output); readln(n,m); for i:=1 to n do begin read(a[i]); f[i]:=maxlongint>>1; end; len:=trunc(sqrt(n)); f[0]:=0; tail:=1; q[1]:=1; f[1]:=1; p[a[1]]:=1; for i:=2 to n do begin k:=i; for j:=1 to tail do if q[j]>p[a[i]] then begin tmp:=q[j]; q[j]:=k; k:=tmp; end else break; if (tail<len)and(q[tail]>p[a[i]]) then begin inc(tail); q[tail]:=k; end; p[a[i]]:=i; for j:=1 to tail do f[i]:=min(f[i],f[q[j]-1]+j*j); end; writeln(f[n]); close(input); close(output); end.