求逆序 二分+树状数组

#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表示取反加一。

大佬AC链接1  大佬AC链接2   树状数组详解

/* 
用树状数组求逆序数:数组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。

猜你喜欢

转载自blog.csdn.net/huanting74/article/details/80225616