牛客網:逆序對(歸併排序)
阿新 • • 發佈:2021-01-02
技術標籤:遞迴
思路:我們知道在歸併排序的逆序對求法中,我們是通過遞迴的方式求解每一段的逆序對,通過這個方法我們似乎可以求解該題,在遞迴的過程中,我們多一個引數用來表示當前遞迴的是第幾層,而這個層數其實就是我們需要找的qi。我們首先預處理出所有層的逆序對數和順序對數,在查詢時我們需要翻轉所有大小為2^(qi)的塊,由於已經預處理過了,因此直接交換相應層的順序對和逆序對的值即可,但是在交換的過程中我們發現其實所有小於2^(qi)的塊的逆序對數量也等於原順序對數量,因此也是需要交換的。除此之外在求解答案的時候我們也需要將所有層的逆序對數加起來。
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; #define ll long long #define maxn (1<<20)+5 int n,m,q[maxn],a[maxn],b1[maxn],b2[maxn]; ll rev[maxn],nor[maxn]; void merge(int id,int l,int r,ll nums[],int arr[]){ if(l==r) return; int i,j,k; ll res=0; int mid=(l+r)/2; merge(id-1,l,mid,nums,arr); merge(id-1,mid+1,r,nums,arr); for(i=l,j=mid+1,k=l;i<=mid && j<=r;){ if(arr[i]<=arr[j]) a[k++]=arr[i++]; else{ res+=mid-i+1; a[k++]=arr[j++]; } } nums[id]+=res; while(i<=mid) a[k++]=arr[i++]; while(j<=r) a[k++]=arr[j++]; for(int i=l;i<=r;i++) arr[i]=a[i]; } ll solve(int x){ for(int i=x;i>=0;i--) swap(nor[i], rev[i]); ll res=0; for(int i=0;i<=n;i++) res+=rev[i]; return res; } int main(void){ while(scanf("%d",&n)!=EOF){ for(int i=0;i<(1<<n);i++){ scanf("%d",&b1[i]); b2[i]=b1[i]; } scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&q[i]); memset(rev,0,sizeof(rev)); memset(nor,0,sizeof(nor)); merge(n,0,(1<<n)-1,rev,b1); reverse(b2,b2+(1<<n)); merge(n,0,(1<<n)-1,nor,b2); for(int i=1;i<=m;i++) printf("%lld\n",solve(q[i])); } return 0; }