- 注意,一个序列的正序对+逆序对永远是常数。
- 最后输出整个逆序对的结果。
- index最后一层必为1;
- 交换的时候顺序对每pow(2,x)交换其实就是预先构建好的正序对交换到逆序对上,然后对逆序对求和。
#include<bits/stdc++.h> using namespace std; void merge(int arr[],int left,int mid,int right,int index,vector<long>& reorder){ vector<int> temp(right-left+1,0); int i = left, j = mid+1, k = 0, origin = left; long c =0 ; while(i<=mid&&j<=right){ if(arr[i]<=arr[j]){//必须小于等于,因为下一个条件是给逆序对的。 temp[k++] = arr[i++]; }else{ temp[k] = arr[j]; c+= mid+1 - i;//加和逆序对的个数 k++; j++; } } for(;i<=mid;){ temp[k++] = arr[i++]; } for(;j<=right;){ temp[k++] = arr[j++]; } // 将临时数组中的元素放入到原数组的指定位置 for(auto num:temp){ arr[origin++] = num; } reorder[index] +=c;//最后赋值 //逆序对个数 } void mergesort(int arr[],int left, int right,vector<long>& reorder,int index){ int mid = (left+right)/2; if(left<right){ mergesort(arr,left,mid,reorder,index-1); mergesort(arr,mid+1,right,reorder,index-1); merge(arr,left,mid,right,index,reorder); } } int main(){ int n,m; cin>>n; long x; vector<long> v,q; int N = 1<<n; //正序数组 int a[N]; //逆序数组 int b[N]; /* order[i] 表示 2^i 的 顺序对个数 reOrder[i] 表示 2^i 的 逆序对个数 比如 i = 1,那么就是每 2 个元素的逆序对的个数,比如 [4,3,2,1], 有 [2,1] [4,3] 两个逆序对,即 reOrder[1] = 2 比如 i = 2,那么就是每 4 个元素的逆序对的个数,比如 [4,3,2,1], 有 [4,3] [2,1] [4,2] [4,1] [3,2] [3,1] 6 个逆序对, 只不过 [2,1] [4,3] 在 i = 1 的时候算过了,因此有 4 个 即 reOrder[2] = 4 我们可以发现,如果大小为 n 的数组是降序的, 那么逆序对个数为 n * (n - 1) / 2 个 比如 [4,3,2,1] 对于 4 来说,后面有 [3,2,1] 3 个元素小于它,那么逆序对个数为 3 对于 3 来说,逆序对个数为 2 对于 2 来说逆序对个数为 1 如果数组是降序的,那么逆序对个数为 (n - 1) + (n - 2) + ... + 1 = n * (n - 1) / 2 而顺序对的个数为 0,但如果我们将其中两个元素交换位置, 减少了 m 个逆序对的同时就会增加 m 个顺序对 这表示 顺序对的个数 + 逆序对的个数 = n * (n - 1) / 2, 即 顺序对 和 逆序对 是互补的 或者这么说,当数组的逆序对个数为 m, 顺序对的个数为 n 那么数组翻转过后,逆序对和顺序对的个数相互交换 即逆序对的个数变为 n, 顺序对的个数变为 m 因此,当我们对 2^i 个元素进行翻转的时候 实际上就是交换它们的顺序对和逆序对个数 */ // order[i] 表示 2^i 的 顺序对个数 //reOrder[i] 表示 2^i 的 逆序对个数 vector<long> order(N+1,0); vector<long> reorder(N+1,0); for(int i =0; i< N;i++){ cin>>x; a[i] = x;//正向添加 b[N-1-i] = x;//反向添加 } //一次归并求逆序对数 mergesort(a,0,N-1,reorder,n); //一次归并求顺序对数(逆着求逆序对得个数,那就是顺序对得个数) mergesort(b,0,N-1,order,n); cin>>m; for(int i =0; i< m;i++){ cin>>x; // 2^i 个元素为一组进行翻转(0不需要翻转) for(int j =1; j<=x;j++){ swap(order[j],reorder[j]); } long count = 0; for(int k =1;k<=n;k++){ count+=reorder[k]; } cout<<count<<endl; } }