题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4911

题目大意:最多可以交换K次,就最小逆序对数

解题思路

逆序数定理,当逆序对数大于0时,若ak<ak+1,那么交换后逆序对数+1,反之-1。

设原始序列最小逆序对数=cnt

那么,交换K次的最小逆序对数max(0,cnt-k)

在求原始序列最小逆序对数上,朴素暴力复杂度O(n^2)不可取

有以下两种O(nlogn)的方法:

①排序内计算:

主要是利用归并排序内的特性,即相邻两个归并序列逆序情况不改变,[5,4,2,1]到[4,5]、[1,2]

在排序纠正逆序之后,4和1,5和2的逆序情况没有改变。利用这个性质,只要在归并排序对两个子序列merge排序时,统计逆序对数即可。

即,边排序,边统计,假设left、right序列是递归传递过来的序列从0开始重新编号之后,初始偏移,i=j=0

当left[i]>right[j]出现逆序情况时,cnt+=(leftnum-i),即当前right[j]元素和left[i]及以后元素都构成逆序对。

归并后,递归继续merge更大的序列。统计复杂度=排序复杂度O(nlogn)

注意归并排序的写法,left尾和right尾要设为inf,这样后跑完的序列会直接和inf比较。

g#include "cstdio"
#include "algorithm"
#define LL long long
using namespace std;
int a[100005];
LL cnt=0;
void merge(int l,int m,int r)
{
    int lnum=m-l+1,rnum=r-m;
    int *LEFT=new int[lnum+1],*RIGHT=new int[rnum+1];
    for(int i=0;i<lnum;i++) LEFT[i]=a[l+i];
    for(int i=0;i<rnum;i++) RIGHT[i]=a[m+1+i];
    LEFT[lnum]=RIGHT[rnum]=0x3fffffff;
    int i=0,j=0;
    for(int k=l;k<=r;k++)
    {
        if(LEFT[i]<=RIGHT[j])
        {
            a[k]=LEFT[i];
            i++;
        }
        else
        {
            a[k]=RIGHT[j];
            j++;
            cnt+=(lnum-i);
        }
    }
}
void mergeSort(int l,int r)
{
    if(l<r)
    {
        int m=(r-l)/2+l;
        mergeSort(l,m);
        mergeSort(m+1,r);
        merge(l,m,r);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        cnt=0;
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        mergeSort(0,n-1);
        printf("%I64d\n",max((LL)0,cnt-k));
    }
}
View Code

相关文章: