排序 计蒜客

题目

排序 计蒜客

分析

**思想:**将一组数从小到大排序,求其最小交换次数,一个单独数字的交换次数,应该就是求这个数前面有多少个数比此数要大的数的个数 ; 总的交换次数,就是求每个数的前面有多少个数比此数大的个数之和。

做到这里,应该想到之前所做的题目,棋子等级,这道题我没有写博文,就没办法贴出来,就是利用树状数组getsum()求一个数前面有多少个数比此数小。

但是题目是求比此数大的个数,而getsum()是求比此数小的个数,这就要好好思考一下了。

正好这道题目的取值范围很恶心,一个整数ai的取值范围是(ai<=10^9),用正常的做法做的话,树状数组就必须开 10^9这么大,毫无疑问,一运行内存就爆炸,但是这里n的取值范围是(n<=500000),这样我们就可以使用离散化这个方法,将原本数据分别映射成1到500000的每个数,这样树状数组就只用开500010就可以了,然而我是将大的数映射成小的值,这样原本求比此数大的个数,就成了求比此数小的个数。

AC代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;

const int MAXN=500001;
int tree[MAXN];//树状数组用于存储离散化后的值,并求出每个值前面有多少个数比此值要大。
int num[MAXN];//原数组,录入数据
int temp[MAXN];//中间数组,存储离散化的结果
map<int,int> mp;//映射关系
int n,m;

bool cmp(int a,int b){
    return a>b;
}
void change(int x){
    for(;x<=m;x+=x&(-x)){
        tree[x]++;
    }
}
int getsum(int x){
    int res=0;
    for(;x;x-=x&(-x)){
        res+=tree[x];
    }
    return res;
}


int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++){//录入n个数据
        scanf("%d",&num[i]);
        temp[i]=num[i];
    }
    sort(temp,temp+n,cmp);//将n个数据从大到小排序
    m=unique(temp,temp+n)-temp;//去除相邻元素重复的值,将此值放入数组尾部,并统计出不重复的元素个数。
    for(int i=0;i<m;i++){//将原数组中的大值映射成小值,如把9->1;
        mp[temp[i]]=i+1;
    }
    int res=0;
    for(int i=0;i<n;i++){
        res+=getsum(mp[num[i]]-1);//统计比此数小的数有多少个,
        change(mp[num[i]]);//将此数插入树状数组
    }
    cout<<res<<endl;
    return 0;
}