Stop the Hollyweb! No DRM in HTML5.

BZOJ1786[AHOI2008]Pair配对

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

Description
小可可和小卡卡想到Y岛上旅游,但是他们不知道Y岛有多远。好在,他们找到一本古老的书,上面是这样说的: 下面是N个正整数,每个都在1~K之间。如果有两个数A和B,A在B左边且A大于B,我们就称这两个数为一个“逆序对”。你数一数下面的数字里有多少个逆序对,你就知道Y岛离这里的距离是多少千米了。 比如说,4 2 1 3 3里面包含了5个逆序对:(4, 2), (4, 1), (4, 3), (4, 3), (2, 1)。 可惜的是,由于年代久远,这些数字里有一部分已经模糊不清了,为了方便记录,小可可用“-1”表示它们。比如说,4 2 -1 -1 3 可能原来是4 2 1 3 3,也可能是4 2 4 4 3,也可能是别的样子。 小可可希望知道,根据他们看清楚的这部分数字,能不能推断出这些数字里最少能有多少个逆序对。
Input
第一行两个正整数N和K。第二行N个整数,每个都是-1或是一个在1~K之间的数。
Output
一个正整数,即这些数字里最少的逆序对个数。
Sample Input
5 4
4 2 -1 -1 3

Sample Output
4

HINT

4 2 4 4 3中有4个逆序对。当然,也存在其它方案得到4个逆序对。

数据范围
100%的数据中,N<=10000,K<=100。
60%的数据中,N<=100。
40%的数据中,-1出现不超过两次。
Source

Day1

先证明一个定理:填的数必然不下降。

我们这里假设有2个空,应填入a,b(1<=a<=b<=k)。

[Part A] [Blank 1 a ] [Part B] [Blank 2 b ] [Part C]

在partA中,记

<a 的个数为A1,=a的个数为A2,(a,b)的个数为A3,=b的个数为A4,>b的个数为A5。

同理记B、C部分。

若填 a b

Pair1=正填的逆序对数=Pair(a,b,c之间)+A3+A4+A5+B1+C1+A5+B5+C1+C2+C3

=Pair(a,b,c之间)+A3+A4+2A5+B1+B5+2C1+C2+C3

若这两个空 填 b a

Pair2=反填的逆序对数=Pair(a,b,c之间)+A5+B1+B2+B3+C1+C2+C3+A3+A4+A5+B3+B4+B5+C1

=Pair(a,b,c之间)+A3+A4+2A5+B1+B2+2B3+B4+B5+2C1+C2+C3

显然Pair2>=Pair1,即若存在两空单调递减,我们把它们换过来逆序对数至少不会增加。

我们先用O(NK)时间预处理出第i位前比j大的数个数more[i,j]与第i位后比j小的数个数small[i,j]。

记f[i,j]为第i个空最后一个填的数为j的 填空的数 与 固定的数之间的逆序对数。

先计算出f[1,j]

f[i,j]=Min{f[i-1,w]+more[b[i],j]+small[b[i],j] | 1<=w<=j} (b[i]为第i个空的位置)

之后找最小的F[-1个数,j]

再加上固定数之间的逆序对数就行了。

O(NK)。懒得用滚动数组了。

 

program p1786;
var
 a,b:array[0..10002] of longint;
 small,more,f:array[0..10002,0..102] of longint;
 n,i,j,k,w,ans,ct,st:longint;

procedure fopen;
  begin
  assign(input,'p1786.in');
  assign(output,'p1786.out');
  reset(input);
  rewrite(output);
end;

Procedure fclose;
  begin
  close(input);
  close(output);
end;

Function min(a,b:longint):longint;
  begin
  if a<b then exit(a) else exit(b);
end;

  begin
  fopen;
  readln(n,k);
  for i:=1 to n do
    read(a[i]);
  readln;

  for i:=1 to n do
    if a[i]=-1 then
      begin
      inc(ct);
      b[ct]:=i;
    end;
  st:=0;
  for i:=2 to n do
    begin
    for j:=1 to k do
      more[i,j]:=more[i-1,j];
    if a[i-1]<>-1 then 
    for j:=1 to a[i-1]-1 do
      inc(more[i,j]);
  end;
  
  for i:=n-1 downto 1 do
    begin
    for j:=1 to k do
      small[i,j]:=small[i+1,j];
    if a[i+1]<>-1 then
      for j:=a[i+1]+1 to k do
        inc(small[i,j]);
  end;

  for i:=1 to n do
    if a[i]<>-1 then
      inc(st,more[i,a[i]]);
  fillchar(f,sizeof(f),$7f);
  for i:=1 to k do
    f[1,i]:=more[b[1],i]+small[b[1],i];
  for i:=2 to ct do
    for j:=1 to k do
      for w:=1 to j do
        f[i,j]:=min(f[i,j],f[i-1,w]+more[b[i],j]+small[b[i],j]);
  ans:=maxlongint;
  for i:=1 to k do
    ans:=min(ans,f[ct,i]);
  if ans>1e8 then ans:=0;
  writeln(ans+st);
  fclose;
end.

 

 

 

 

posted on 2012-12-11 20:40  灰天飞雁  阅读(1034)  评论(2编辑  收藏  举报

填写您的邮件地址,订阅我们的精彩内容:  点击这里给我发消息

添加到收藏夹