离散化+例题
离散化
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;
}

浙公网安备 33010602011771号