Minimum Inversion Number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6820 Accepted Submission(s): 4162
Problem DescriptionThe inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.
For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:
a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)
You are asked to write a program to find the minimum inversion number out of the above sequences.
InputThe input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.
OutputFor each case, output the minimum inversion number on a single line.
Sample Input
101 3 6 9 0 8 5 7 4 2
Sample Output
16又是一個經典的線段樹,首先要知道如何求一個序列的逆序——比如2514 : 2之前沒有數大於它,所以為0, 5之前也沒有數大於它,所以為0, 1之前2,5都大於它,所以為2, 4之前只有5大於它,所以為1, 因此2514的逆序數為:0 + 0 + 2 + 1 = 3; 從前面的描述中,我們可以發現,只要依次累計前面有幾個數大於當前數即可。於是我們可以用一個數組l[n](初始化為0),然後每讀取一個數x[i] 就標識l[x[i]] += 1 , 但是在標識之前我們先要統計有多少個數大於x[i],即累計l[x[i]+1] + ... +l[n-1],很顯然累計的時間複雜度為O(N),我們能不能在快點呢? 那麼如何求最小逆序數呢?(我這裡假設一個序列中每個數字都不同) 若abcde...的逆序數為sum,那麼bcde...a的逆序數是多少?我們假設abcde...中小於a的個數為x[i] , 那麼大於a的個數就是n - x[i],當把a移動左移一位時,原來比a大的現在都成了a的逆序對,故減少了x[i]+1,即 相比而言總共加了n-x[i]-x[i]-1;其實,線段樹只有開始用了一次,後面求最小值就沒用了,所以用暴力應該也能過,sum再更新N—-1次就可以得到最小值了,不算複雜!於是我
#include <iostream>#include<stdio.h>using namespace std;#define N 50000int l[N<<2],x[N];void build(int num ,int s,int e){ l[num]=0; if(s==e) return ; int mid=(s+e)>>1; build(num<<1,s,mid); build(num<<1|1,mid+1,e);}int query(int num ,int s,int e ,int a,int b){ if(a<=s&&b>=e) return l[num]; int mid=(s+e)>>1; int ret=0; if(mid>=a) ret+=query(num<<1,s,mid,a,b); if(mid<b) ret+=query(num<<1|1,mid+1,e,a,b); return ret;}void update(int num ,int s,int e,int a){ if(s==e) { l[num]++; return ; } int mid=(s+e)>>1; if(a<=mid) update(num<<1,s,mid,a); else update(num<<1|1,mid+1,e,a); l[num]=l[num<<1]+l[num<<1|1];}int main(){ int n,i; while(scanf("%d",&n)!=EOF) { build(1,0,n-1); int sum=0; for(i=0;i<n;i++) { scanf("%d",&x[i]); sum=sum+query(1,0,n-1,x[i],n-1); update(1,0,n-1,x[i]); } int re=sum; for(i=0;i<n-1;i++) { sum+=n-x[i]-x[i]-1; if(sum<re) re=sum; } printf("%d\n",re); } return 0;}