离散化

背景:离散化一词常常见到,甚至很多算法题不自觉的都会用到,比如hashmap,根据value找key等(内置find效率较低),根据acwing的例题总结了一下:

例题:https://www.acwing.com/problem/content/804/

所谓离散化,就是将一些定义域很大的数,但总数较小,就可以映射到一个新的数组。

假设我们从输入中得到案例的key-value,往往想搞成以下类似形式:

{0,1},{1,100},{2,1000},{3,2234},{4,99999999},{5,100}...

但是题设是这样的形式

{1,?},{100,?},{1000,?},{2234,?},{99999999,?},{100,?}...

ps.每对数中第一个为数组下标,第二个是增加值的大小

这样的k-v关系使得我们很难创建一个如此之大的数组空间。于是得从数据结构下手,创建vector<pair<int,int>>,批量地增加值再输出区间和 ,我们可以使用前缀和来有效降低轮询时间复杂度。


我们将k-v关系保存在一个存储pair的vector 记为add[]

vector value {{1,0},{100,2},{1000,3},{2234,3},{99999999,4},{100,5}...}

vector key 0 1 2 3 4 5

再单独创建数组记为alls[]={1,100,1000,2234,999999999,100}

我们注意到此时alls[]有两个相同的值,一般情况下我们可能会需要对alls进行去重。(sort+erase(unique,end))

方式一:sort+unique

vector <int> v;
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end);

unique(it1,it2);//先将所有重复元素排在尾部,返回第一个重复元素的指向迭代器

方式二:生成时count()==0判断是否有,无重复再push_back。

去重后:alls[]={1,100,1000,2234,99999999} 此时值就是唯一

此时如果遍历 add, it.first 即为值,本题中我们将it.second设为累加值

体现离散化思想的操作来了:

我们使用一个 int find(int value)函数来返回一个 (0,1,2,...n)区间的值。传入it.first(这里是找下标操作,可以用stl里面的find内置函数解决,但复杂度高)

构造a[N]存储累加值,大小由key范围决定

i 1 2 3 4 2.....

a[i] 100 40 22 12 40......

(it.first,it.second) {1,100} {100,20} {1000,22} {2234,12} {100,20}...... (先看这一排,再去找i,a[i])

s[i] 100 120 142 154....

ps. i=2时a[i]和it.second不等是因为有重复的it.first=100;

最后前缀和就可以用在轮询区间和上了。

讲了这么多,其实一个图就能讲明白.jpg:

​ |---------------------------------|----------------------------------|

索引: -777777 -1 0 1 999999

转换为:

​ |{-777777,?} {-1,?} {1,?} {9999999 ,?}|

​索引: 0 1 2 3

加上

​ {-777777 ,-1 , 0 , 1 , 999999}

代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;

const int N = 300010;

int n, m;
int a[N], s[N];

vector<int> alls;
vector<PII> add, query;

int find(int x)
{
    int l = 0, r = alls.size() - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1;
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; i ++ )
    {
        int x, c;
        cin >> x >> c;
        add.push_back({x, c});

        alls.push_back(x);
    }

    for (int i = 0; i < m; i ++ )
    {
        int l, r;
        cin >> l >> r;
        query.push_back({l, r});

        alls.push_back(l);
        alls.push_back(r);
    }

    // 去重
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());

    // 处理插入
    for (auto item : add)
    {
        int x = find(item.first);
        a[x] += item.second;
    }

    // 预处理前缀和
    for (int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i];

    // 处理询问
    for (auto item : query)
    {
        int l = find(item.first), r = find(item.second);
        cout << s[r] - s[l - 1] << endl;
    }

    return 0;
}

上一篇:C++STL迭代器


下一篇:deque容器基本操作