离散化及两种主要实现

离散化

所谓离散化就是当我们需要存一定数量的数组时,数组开不了那么大,但是数字的种数不多,故离散化就是改变了数据数量的相对大小

去重离散化

给定一个包含大量重复元素数组,而我们用到其中的每一个数即可,去重显然是更好的选择

unique函数

头文件:#include

注意:使用前一定要先将无序的列表排序,使重复的元素相邻

原理:

  • 该函数的作用是“去除”容器或者数组中相邻元素的重复出现的元素
    PS:这里的去除并非真正意义的删除,而是将重复的元素放到容器的末尾,返回值是去重之后的尾地址
  • 函数的返回值为开始重复的第一个元素

举例:

int数组a[7](排过序的):1,1,2,3,3,3,6

使用过unique(a,a+7),数组变为1,2,3,6,3,3,6

(PS:unique只改变了前4(即不重复元素的个数)个数字,后边的不变)

而且返回的就是上面标记数字的地址

因此常常用来求出不重复元素:

int a[n]sort(a,a+n);
int num=unique(a,a+n)-a; //按上面例子来看num就是4
for(int i=0;i<num;i++)
    cout<<a[i]<<endl;

映射离散化

为什么给它起名映射离散化呢,因为这种离散化不减少元素的个数,而且是采用给数组每个数映射一个id的方法间接得到我们需要的结果。例如一个只有四个数的数组a:

a[i] = 104,1e6,1e8,1e10

而如果我们给每个数一个映射将a数组转化为b[i] = 1,2,3,4,我们可以发现数组元素之间的相对大小关系是不影响的

还有如果我们要得到一堆数据中每个数i(i很大)作为数组下标的值,而这些数据总数却不太多。显然直接开数组是存不下的,因此就需要离散化,下面举一个很实用的例子

逆序对

对于一个数组a若 i > j && a[i] < a[j] 那么称a[i]与a[j]是一个逆序对

其实逆序对就是求每个数前面有多少个比它大的数。暴力求解显然超时,那么我们这样想,如果给定一组乱序数,求解每个数前面有多少比它小的,那么可以用树状数组。树状数组就是可以很方便维护前缀和,从而得到每个数前面有多少比它小的,但是我们如果用数组存每个数作为下标,开不了很大的数组怎么办?离散化!

究竟如何离散化,我们先给每个数按输入顺序分配一个id,可以用结构体保存,例如

id 1 2 3 4
a[i] -1 19 2 5

那么我们按a[i]从小到大排序可以得到

id 1 3 4 2
a[i] -1 2 5 19

那么我们可以发现原始数组的逆序对数是2,而按id来看得到的逆序对个数也为2。于是我们只要按id作为下标存到树状数组中,我们每次查询得到某个id前面有多少个比它小的,那么id-getSum(a[i].id)就可以得到前面有多少个比它大的,但是这样显然太麻烦,我们如果直接把a[i]按id从大到小排序,查询每个id前面都多少个比它大的不就行了?但是这时要注意先更新先查询的区别,如果先更新的话,该节点本身以及之前的树状数组对应值都会更新,因此要getSum(a[i].id-1)。但是如果先查询再更新的话就直接getSum(a[i].id)即可

洛谷逆序对参考代码:

#include <iostream>
#include <algorithm>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
const int maxn=5e5+10;
struct node{
    int id,v;
}a[maxn];
int t[maxn];
ll n;

bool cmp(node &a,node &b){
    if(a.v==b.v) return a.id>b.id;
    return a.v>b.v;
}

void update(int i){
    while(i<=n){
        t[i]++;
        i+=lowbit(i);
    }
}

ll getSum(int i){
    ll ans=0;
    for(;i;i-=lowbit(i))
        ans+=t[i];
    return ans;
}

int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].v);
        a[i].id=i;
    }
    sort(a+1,a+1+n,cmp);
    ll ans=0;
    for(int i=1;i<=n;i++){
        ans+=getSum(a[i].id); //先查询的话直接查询每个id即可
        update(a[i].id); 
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(算法)