zoukankan      html  css  js  c++  java
  • 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.
    
  • 相关阅读:
    contes配置nginx教程
    jquery 图片放大镜,草稿版
    VUE学习第四次
    VUE学习 第三次
    ryu
    ovs & ryu & mininet
    centos安装mininet 和卸载
    端口镜像
    数据中心网络监控小结
    5、Kafka生产过程分析
  • 原文地址:https://www.cnblogs.com/htfy/p/2813497.html
Copyright © 2011-2022 走看看