这道题目有两种做法求逆序数,一是归并排序,而是线段树或树状数组,两种都实现了一下。
这道题还需要对数据进行离散化,注意的地方写在注释里了。
归并排序
<span style="font-size:18px;">#include<stdio.h> #include<iostream> #include<string.h> #include<algorithm> using namespace std; #define MAX 100010 int a[MAX]; int b[MAX]; struct node{ int x; //位置 int h; //大于 int l; //小于 }num[MAX]; int n; int find(int x); void merge(int l,int r); int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[i] = a[i]; num[i].h = 0; //必须置零 } sort(b+1,b+n+1); //离散化 for(int i=1;i<=n;i++){ int k = find(a[i]); a[i] = k; b[k]--; //保证后边与他相同的数不受影响,因为查找最靠左的数,不会影响前边的数 num[a[i]].x = i; //保存离散后的数的原位置 } merge(1,n); long long ans = 0; for(int i=1;i<=n;i++){ num[i].l = i - (num[i].x - num[i].h); //数字的排列位置-(数字位置-大于该数字的数) ans += (long long)(num[i].l + num[i].h) * (num[i].l + num[i].h + 1)/2; //最终错误点,强制类型转换,否则会只得70%的分 } printf("%I64d\n",ans); return 0; } //归并排序 void merge(int l,int r){ if(l==r) return ; int mid = (l+r)/2; merge(l,mid); merge(mid+1,r); int i = l,j = mid + 1,k = 0; while(i<=mid&&j<=r){ if(a[i]<=a[j]){ //注意 = b[k++] = a[i++]; } else{ b[k++] = a[j]; num[a[j++]].h += mid - i + 1; //计算该节大于a[j]的数 注意+= } } while(i<=mid){ b[k++] = a[i++]; } while(j<=r){ b[k++] = a[j++]; } for(int p=l,q = 0;p<=r;p++,q++){ a[p] = b[q]; } } //对b二分查找 int find(int x){ int l = 1,r = n,mid; while(l<r){ mid = (l+r)/2; if(b[mid]==x){ if(b[mid-1]<x){ return mid; } else{ r = mid-1; } } else if(b[mid]>x){ r = mid-1; } else{ l = mid + 1; } } return l; }</span>
线段树做法,插入和计算合并用了一个函数,即线段树+离散化
<span style="font-size:18px;">#include<iostream> #include<stdio.h> #include<math.h> #include<string.h> #include<vector> #include<list> #include<algorithm> using namespace std; #define MAX 100009 #define lson l,m,level*2 #define rson m+1,r,level*2+1 int a[MAX]; int b[MAX]; int insert(int l,int r,int level,int x); int found(int x); struct node{ int x; int l; int h; }num[MAX]; int n; int tree[MAX*5]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[i] = a[i]; num[i].h = 0; } //离散化 sort(b+1,b+1+n); for(int i=1;i<=n;i++){ int t = found(a[i]); a[i] = t; num[t].x = i; b[t]--; } //线段树插入 memset(tree,0,sizeof(tree)); long long sum = 0; for(int i=1;i<=n;i++){ num[a[i]].h = insert(1,n,1,a[i]); //点插入(按照原顺序,计算比该点大的数的数目) num[a[i]].l = a[i] - (num[a[i]].x - num[a[i]].h); sum += (long long)(num[a[i]].h + num[a[i]].l)*(num[a[i]].h + num[a[i]].l + 1)/2; } printf("%I64d\n",sum); return 0; } int insert(int l,int r,int level,int x){ if(l==r&&l==x){ tree[level] = 1; return 0; //注意该点不计入 } int m = (l+r)>>1; int temp; if(x<=m){ temp = insert(lson,x); temp += tree[level*2+1]; //加上比他大的数 } else if(x>m){ temp = insert(rson,x); } tree[level] = tree[level*2] + tree[level*2+1]; //更新tree return temp; } //二分 int found(int x){ int l = 1,r = n; int mid; while(l<r){ mid = (l+r)/2; if(b[mid]==x){ if(b[mid-1]!=x) return mid; else r = mid-1; } else if(b[mid]>x){ r = mid-1; } else if(b[mid]<x){ l = mid+1; } } return l; }</span>