bzoj2058: [Usaco2010 Nov]Cow Photographs(逆序对)
Posted Sakits
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2058: [Usaco2010 Nov]Cow Photographs(逆序对)相关的知识,希望对你有一定的参考价值。
题目大意:给出n个数的序列,每次可以交换相邻的两个数,问把序列变成编号i在编号i+1左边,编号1在编号n右边(一个环)最少需要多少步。如:35421最少交换两次变为34512。
一开始看到这题,只会O(n²),后来仔细想了一下,妙啊,妙不可言。
首先我们求出逆序对,即为这个序列变成升序排列的最小次数,问题就在于23451这类的怎么求了。突然,灵稽一动,我们只要把1改成6,然后就可以算出23456的答案,即23451的答案。至于方法,就是我们通过原序列逆序对数量减去1产生的逆序对数量,然后加上给序列添加6产生的逆序对数量,就是23451的答案了。接下来同理,把2改成7,于是我们就可以递推出34512的答案了,以此类推算出所有情况的答案。。。总结一下方法就是把上一次算出来的答案减去现在序列里最小数产生的逆序对数量,然后加上给序列添加最大数+1产生的逆序对数量。
显然,序列里没有一个数比最小数小(一句废话>_<),所以它产生的逆序对数量就是最小数的位置-1;显然,序列里没有一个数比最大数大(两句废话>_<),所以最大数产生的逆序对数量就是这个数后面的数的数量,即n-最大数的位置,也就是ans[i]=ans[i-1]-(pos[i]-1)+(n-pos[i]),然后我们就输出min(ans[i])就行啦。
妙啊,妙不可言
代码如下:
uses math; var n,i:longint; ans,sum:int64; a,b,c:array[0..1000000]of int64; procedure merge(l,m,r:longint); var l1,m1,k,i:longint; begin l1:=l;m1:=m+1;k:=l; while (l1<=m)and(m1<=r)do begin if a[l1]<=a[m1] then begin b[k]:=a[l1]; inc(l1);inc(k); end else begin b[k]:=a[m1]; inc(m1);inc(k); ans:=ans+m-l1+1; end; end; while l1<=m do begin b[k]:=a[l1];inc(l1);inc(k); end; while m1<=r do begin b[k]:=a[m1];inc(m1);inc(k); end; for i:=l to r do a[i]:=b[i]; end; procedure sort(l,r:longint) ; var mid:longint; begin if l=r then exit; mid:=(l+r)>>1; sort(l,mid); sort(mid+1,r); merge(l,mid,r); end; begin readln(n); for i:=1 to n do begin read(a[i]); c[a[i]]:=i; end; sort(1,n); sum:=ans; for i:=1 to n do begin sum:=sum-(c[i]-1)+(n-c[i]); ans:=min(ans,sum); end; writeln(ans); end.
以上是关于bzoj2058: [Usaco2010 Nov]Cow Photographs(逆序对)的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj2058][Usaco2010 Nov]Cow Photographs_树状数组_动态规划
bzoj2058: [Usaco2010 Nov]Cow Photographs(逆序对)
bzoj2060[Usaco2010 Nov]Visiting Cows 拜访奶牛*
[codevs1380]没有上司的舞会([BZOJ2060][Usaco2010 Nov]Visiting Cows 拜访奶牛)