BZOJ 1584 [Usaco2009 Mar] Cleaning Up

来源:互联网 发布:软件开发月报 编辑:程序博客网 时间:2024/03/29 02:14

http://www.lydsy.com/JudgeOnline/problem.php?id=1584


首先感谢were的详细解答!

然后感谢网上某一个code= - =(我的code很像那个大神的0 0我主要是详细讲解下0 0)

我们看题目= - =可以发现。

最坏的情况是分成N段,这样答案就是N

所以 我们分成的每一段里不同的个数必定不能超过sqrt(n)要不然代价还不如分成N段

这样。当我们在A[I]处时,我们可以有sqrt(n)个不同的状态转移得到

(设B[J]为:从B[J]+1到I中不同数量不超过J的最小的位置)

这样 我们的F[I]=min(F[B[J]]+J*J);(1<=j<=sqrt(n))

但是如何维护B呢?

我们可以用C数组维护C[J]表示 [b[j+1],i] 这 个区间有多少个不同的数

P[A[I]]记录A[I]这个数之前在哪个位置出现过

如果P[A[I]]<=B[J] 那么就说明a[i]在[B[J]+1,i-1]没出现过,那么我们只能从B[J]+1开始找一个删掉..这个我们用暴力..(were大神说这个是均摊o(1))

设X=B[J]+1 我们找啊找啊 每次X:=X+1。直到找到一个在我们找到的a[x]在[X,I]区间只出现了一次的数,=w=code:

var  x,n,m,i,j,k:longint;  f,a,b,c,p:array[-1..40005] of longint;begin  read(n,m);  for i:=1 to n do    begin  read(a[i]);  f[i]:=maxlongint div 2;end;  for i:=1 to n do p[i]:=-1;  fillchar(b,sizeof(b),0);  fillchar(c,sizeof(c),0);  k:=trunc(sqrt(n));  for i:=1 to n do    begin   for j:=1 to k do    if p[a[i]]<=b[j] then c[j]:=c[j]+1;  p[a[i]]:=i;  for j:=1 to k do    if c[j]>j then   begin    x:=b[j];x:=x+1;while p[a[x]]>x do x:=x+1;    b[j]:=x;    c[j]:=c[j]-1;      end;  for j:=1 to k do    if f[i]>(f[b[j]]+j*j) then f[i]:=f[b[j]]+j*j;end;  writeln(f[n]);end.


原创粉丝点击