离散化

· · 个人记录

离散化

1、进行离散化的目的

对于一些值域很大,但贡献很小的数组,我们就可以对数组进行离散化来优化空间复杂度。以达到减少空间损耗的目的。

2、离散化基本操作

(1)将待离散化的数据用一个二元组(下标,值)储存在vector中
(2)进行排序,去重操作
(3)遍历每一个元素,用find函数确定其离散化后的坐标,值存到一个新的数组里
(4)注: 新数组的下标是经离散化后的,值是原pair里的值

3、例题--区间和

Solution

很明显,前缀和裸题。但整个区间范围是$ -1*10^9--1*10^9 $,爆的彻彻底底。观察数据,$ 1<=n<=1*10^5 $ 而且在不进行添加操作前,整个区间任何一个点都是0,所以对题目有贡献的区间最多只有$ 1*10^5 $个。非常符合离散化的特点,可以进行离散化。

code

标准代码

/*
离散化,用于处理某些值域很大,但是有用数据很分散的题目 -----> 可以很大程度上减少空间的损耗 
*/
#include<bits/stdc++.h>
#define m_k make_pair
#define p_b push_back
#define maxn 100010
using namespace std;
int n,m;
int a[maxn],sum[maxn];//---->离散化后的数组 
vector<pair<int,int> >add;
vector<int>alls;//---->储存所有待离散化的值 
vector<pair<int,int> >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; //----->由于前缀和计算方法从1开始计算,所以离散化后的数组也要从坐标1开始储存,原alls是从0开始的,所以要在其基础上加1 
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x,c;
        cin>>x>>c;
        add.p_b(m_k(x,c));
        alls.p_b(x);
    }
    for(int i=1;i<=m;i++)
    {
        int l,r;
        cin>>l>>r;
        query.p_b(m_k(l,r));
        alls.p_b(l);
        alls.p_b(r);
    }
    sort(alls.begin(),alls.end());
    alls.erase(unique(alls.begin(),alls.end()),alls.end());
    for(auto item : add) //已经去重之后的 alls数组(vector模拟的数组姑且叫他数组吧) 
    //高级的for循环写法,用stl容器 
    {
        int x=find(item.first);//--->找到这个所对应的离散化后的值
        a[x]+=item.second; 
    }
    for(int i=1;i<=alls.size();i++)
        sum[i]=sum[i-1]+a[i];//--->处理一个前缀和数组,离线查找每个区间的总和 
    for(auto item : query)
    {
        int l=find(item.first);
        int r=find(item.second);//--->同理找到下标对应的离散化后的值 
        cout<<sum[r]-sum[l-1]<<endl;// 闭区间[ ],包含两个端点 
    } 
    return 0;
}

STL二分方法

#include<bits/stdc++.h>
#define maxn 300005
using namespace std;
vector<pair<int,int> >add;
vector<int>alls;
vector<pair<int,int> >query;
int n,m; 
int sum[maxn],a[maxn];//前缀和   离散化后的数组 
int find(int x)
{
    return lower_bound(alls.begin(),alls.end(),x)-alls.begin()+1; // 妥协了,还是从1开始存吧 
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x,c;
        cin>>x>>c;
        add.push_back(make_pair(x,c));
        alls.push_back(x);
    }
    for(int i=1;i<=m;i++)
    {
        int l,r;
        cin>>l>>r;
        query.push_back(make_pair(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++)
        sum[i]=sum[i-1]+a[i];
    for(auto item : query)
    {
        int l=find(item.first),r=find(item.second);
        cout<<sum[r]-sum[l-1]<<endl;//闭区间,取两个端点 
    } 
    return 0;
}

4、结语

一晚上的奋斗成果,一晚上才写这么点,我太垃了~~
跑路了~~

5、永远不要怀疑y总

学长给我发了一个比较常见的离散化方式,现在我要证明,y总的离散化没有任何问题,见例题

P1995 程序自动分析

题意不想再分析了,方法是输入时将相等的和不等的分成两组,之后进行离线操作。循环遍历相等的组,用并查集维护他们的相等关系,再遍历不等的组,利用并查集判断每对数是否相等,若相等,说明不等关系不成,输出“NO”,打上标记。最后判断是否存在标记,若无,输出“YES”。

code

/*
solution 
离散化后用并查集维护整个fa数组
将相等和不等分别储存,最后离线查找不等 
*/
#include<bits/stdc++.h>
#define maxn 1000001
#define mk make_pair
#define pb push_back
using namespace std;
vector<pair<int,int> >add;
vector<pair<int,int> >query;
vector<int>alls;// 使劲往里放就行,反正最后都得去重,不放白不放(滑稽) --->我错了,TLE了 
int m,n;
bool cb;
int fa[maxn]; 
int find(int x)
{
    if(fa[x]==x)
        return x;
    return fa[x]=find(fa[x]);
}
int check(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;   
}
signed main()
{
    std::ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>m;
    while(m--)
    {
        cb=false;
        add.clear();
        query.clear();
        alls.clear();
        cin>>n;
        while(n--)
        {
            int pd,y,x;
            cin>>x>>y>>pd;
            if(pd==1)
            {
                add.pb(mk(x,y));
                alls.pb(x);
                alls.pb(y);
            }
            else 
            {
                query.pb(mk(x,y));
                alls.pb(x);
                alls.pb(y);
            }
        } 
        sort(alls.begin(),alls.end());
        alls.erase(unique(alls.begin(),alls.end()),alls.end());
        for(int i=0;i<alls.size();i++)
            fa[i]=i;
        for(auto item : add)
        {
            int x=check(item.first),y=check(item.second);
            fa[find(x)]=find(y);
        }
        for(auto item : query)
        {
            int x=check(item.first),y=check(item.second);
            if(find(x)==find(y))
            {
                cout<<"NO"<<endl;
                cb=true;
                break;
            }
        }
        if(!cb)cout<<"YES"<<endl;
    }
    return 0;
 } 

6、再次结语

“实践出真知”,板子并不适合所有题。

y总yyds!!!!