BZOJ_2058_[Usaco2010 Nov]Cow Photographs_逆序对
题意:
奶牛的图片 Farmer John希望给他的N(1<=N<=100,000)只奶牛拍照片,这样他就可以向他的朋友炫耀他的奶牛.这N只奶牛被标号为1..N. 在照相的那一天,奶牛们排成了一排.其中第i个位置上是标号为c_i(1<=c_i<=N)的奶牛.对于奶牛的站位,Farmer John有他自己的想法. FJ是这么想的,标号为i(1<=i<=n-1)的奶牛只能站在标号为i+1的奶牛的左边,而标号为N的奶牛只能站在标号为1的奶牛的左边.当然,没有牛可以站在队列中最左边的奶牛的左边了.也就是说,最左边的奶牛编号是随意的. 这些奶牛都非常的饿,急切的希望吃到FJ承诺的在拍照后的大餐,所以FJ想尽快的拍照.奶牛们的方向感非常的不好,所以FJ每一分钟只可以选择相邻的两只奶牛然后让他们交换位置.FJ最小需要多少时间就能使奶牛站成一个可以接受的序列? 比方说一个有5只奶牛的例子,一开始序列是这样的: 左边 右边 3 5 4 2 1 第一分钟,FJ可以交换第二队奶牛(即5和4),交换后的队列: 3 4 5 2 1 第二分钟,FJ交换最右边的一对,序列变成这样: 3 4 5 1 2 这样,只用了2分钟,就是序列变为了一个FJ所希望的序列.
分析:
我们分析一下样例。首先将序列变成12345需要的步数为序列的逆序对个数,那么考虑如何将序列变成23451,我们把原序列的1变成6,发现只需要求新序列的逆序对,新序列的逆序对其实就是减去1对序列的贡献再加上6对序列的贡献,只需要维护一下每个数在原序列中出现的位置就可以转移,34512同理。那么我们可以递推求出每个最后一个数为i的序列的逆序对,取min既是答案。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 using namespace std; 5 #define N 100050 6 int n,a[N],c[N],b[N]; 7 #define LL long long 8 void fix(int x) 9 { 10 while(x<=n) 11 { 12 c[x]++; 13 x+=x&(-x); 14 } 15 } 16 int inq(int x) 17 { 18 int re=0; 19 while(x) 20 { 21 re+=c[x]; 22 x-=x&(-x); 23 } 24 return re; 25 } 26 int main() 27 { 28 LL ans=0; 29 scanf("%d",&n); 30 for(int i=1;i<=n;i++) 31 { 32 scanf("%d",&a[i]); 33 b[a[i]]=i; 34 ans+=(i-1-inq(a[i])); 35 fix(a[i]); 36 } 37 LL now=ans; 38 for(int i=1;i<n;i++) 39 { 40 now=now-b[i]+1+n-b[i]; 41 ans=min(ans,now); 42 } 43 printf("%lld",ans); 44 }