离散化+例题

离散化

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!!!!

posted @ 2021-11-14 21:18  Simon_...sun  阅读(214)  评论(0)    收藏  举报