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.