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.