求逆序 二分+樹狀陣列
阿新 • • 發佈:2019-02-01
#include<iostream> #include<stdio.h> using namespace std; int a[100001],n,temp[100001]; long long ct=0; void merg(int first,int last){ int mid=(first+last)/2; int i=first,j=mid+1,k=first; while(i<=mid&&j<=last){ if(a[i]>a[j]){ temp[k++]=a[j++];//把小的放進來。 //由於是有序的,所以i之後的肯定也都比j大 ,所以直接加上即可。 ct+=mid-i+1; }else{ temp[k++]=a[i++]; } } //把剩下的按序放進陣列。 while(i<=mid){ temp[k++]=a[i++]; } while(j<=n){ temp[k++]=a[j++]; } for(int u=first;u<=last;u++) a[u]=temp[u]; } void mergeSort(int first,int last){ if(first>=last)return; if(first<last){ int mid=(first+last)/2; mergeSort(first,mid); mergeSort(mid+1,last); merg(first,last); } } int main(){ cin>>n; for(int i=0;i<n;i++){ cin>>a[i]; } mergeSort(0,n-1); cout<<ct; //printf("%I64d",ct); }
我寫的二分的方法,但是會超時。。。然後看了大佬們的樹狀陣列,又複習了樹狀陣列,它是便於查詢與更新,m次更新複雜度為mlgn,時間複雜度低,效率高,c[i]的意思是它包括從i起(包括i)的lowbit(i)[i&-i],個數的和,-i表示取反加一。
PS:我真的是不太明白這一句:/* 用樹狀陣列求逆序數:陣列A代表數字i是否在序列中出現過, 如果陣列i已經存在於序列中,則A[i]=1,否則A[i]=0, 此時Query(i)返回值為在序列中比數字i小的元素的個數, 假設序列中第i個元素的值為a, 那麼前i個元素中比i大的元素的個數為i-Query(a). */ #include <cstdio> #include <cstring> #define MAXN 100000 using namespace std; int n,tree[MAXN]; int lowbit(int i) { return i&(-i); } void update(int i,int x) { while(i<=n) { tree[i]=tree[i]+x; i=i+lowbit(i); } } int query(int n) { int sum=0; while(n>0) { sum+=tree[n]; n=n-lowbit(n); } return sum; } int main () { while(scanf("%d",&n)!=EOF) { int a,ans=0; memset(tree,0,sizeof(tree)); for(int i=1;i<=n;i++) { scanf("%d",&a); update(a,1); ans+=i-query(a); } printf("%d\n",ans); } return 0; }
ans+=i-query(a);
真的不明白,query(a)表示到a為止的,不明白,再想想。。。。明白了:
query(a)表示輸入當前的狀態時,比a小的元素的個數,那麼用i(當前計算元素所處的位置),減去小於它的元素個數,剩下的便是大於等於它的元素個數,那麼就求出了當前數的逆序,對每一個輸入的數,做判斷。
求序列的逆序對每個數求其左邊大於它的元素的個數。該演算法的複雜度O(nlogn),對n個數進行遍歷,更新樹狀陣列複雜度為logn。