第一種方法是使用歸並排序的方法:
#include<cstdio> #include<algorithm> #define maxn 1000 using namespace std; int n,A[maxn],temp[maxn],ans; void merge(int L1,int R1,int L2,int R2) { int k=0,i=L1,j=L2; while(i<=R1 && j<=R2) { if(A[i]<=A[j]) temp[k++]=A[i++]; else if(A[i]>A[j]) { ans+=(R1-i+1); temp[k++]=A[j++]; } } while(i<=R1) temp[k++]=A[i++]; while(j<=R2) temp[k++]=A[j++]; for(i=0;i<k;i++) A[L1+i]=temp[i]; } void mergeSort(int L,int R) { if(L<R) { int mid = (L+R)/2; mergeSort(L,mid); mergeSort(mid+1,R); merge(L,mid,mid+1,R); } } int main(void) { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&A[i]); mergeSort(0,n-1); printf("%d",ans); return 0; }
第二種方法是使用樹狀數組的方法:
假設有n個數(n<=100000)組成a1,a2,a3,....,an,題目要求完成兩種操作100000次:
1.能夠查詢某段區間的和;2.能夠隨時更新某個數的值。
普通數組:使用普通數組存儲n個數,這個時候查詢某段區間的值的時間復雜度將非常的大;
輔助數組:如果使用輔助數組sum存儲n個數,那么區間的[i,j]的和就是sum[j]-sum[i-1],時間復雜度非常的低
但是如果修改一個數組的元素,那么這個時候的修改的時間復雜度非常高。
樹狀數組:能夠存儲一定的信息,查詢和修改的時間復雜度較低,具有和線段樹類似的優點。
存儲的n個元素用數組a存儲,使用t數組作為樹狀數組。t[x]存儲t[x]管轄的長度的數據的和,
這里需要談論t[x]管轄的長度length,我們設置length為2^k(k是x的二進制末尾0的個數),例如4的末尾有兩個0,
那么t[4]=a[1]+a[2]+a[3]+a[4], 5的末尾有0個0,那么t[5]=a[5].
使用x&(-x)計算t[x]管轄的長度length.(或者使用x^(x-1)也是可以的)
使用函數int sum(int k)計算a[1]+a[2]+...+a[k]的值。
使用函數update(int k,int change)更新所有包含a[x]的t[i],讓t[i]+=change;
#include<cstdio> #define maxn 100010 int n,a[maxn],t[maxn],ans; int sum(int k) { int s = 0; for(int i=k;i>0;i-=i&(-i)) s+=t[i];//所求的區間長度和 = 當前管線的區間和 + 剩下的區間長度和 return s; } void update(int k,int change) { for(int i=k;i<=n;i+=i&(-i)) t[i]+=change;//每一次循環就是找到了下一個包含k的區間對應的t[i] } int main(void) { int num; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&num); a[num]++; update(num,1); ans+=i-sum(num);//sum(num)計算的是小於等於num的總數,當前總數i-sum(num)大於num的元素的個數,就是逆序的個數。 } printf("%d",ans); return 0; }