1. 程式人生 > 其它 >牛客網:逆序對(歸併排序)

牛客網:逆序對(歸併排序)

技術標籤:遞迴

思路:我們知道在歸併排序的逆序對求法中,我們是通過遞迴的方式求解每一段的逆序對,通過這個方法我們似乎可以求解該題,在遞迴的過程中,我們多一個引數用來表示當前遞迴的是第幾層,而這個層數其實就是我們需要找的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;
}