#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表示取反加一。
/* 用树状数组求逆序数:数组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; }PS:我真的是不太明白这一句:
ans+=i-query(a);真的不明白,query(a)表示到a为止的,不明白,再想想。。。。
明白了:
query(a)表示输入当前的状态时,比a小的元素的个数,那么用i(当前计算元素所处的位置),减去小于它的元素个数,剩下的便是大于等于它的元素个数,那么就求出了当前数的逆序,对每一个输入的数,做判断。
求序列的逆序对每个数求其左边大于它的元素的个数。该算法的复杂度O(nlogn),对n个数进行遍历,更新树状数组复杂度为logn。