手动实现离散化

离散化同有的特征就是数据范围特别大 难以开数组实现 但是能够提出有用贡献的数很少

我们就是利用这样一个特性而把它变成我们可以解决的问题有些时候离散操作是可以通过map实现的但是运行效率不高 难免出现超时所以要学习手动离散化的操作 通过模板题来讲解具体操作

模板题:区间和

题目描述:

假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。

现在,我们首先进行 n 次操作,每次操作将某一位置 x 上的数加 c。

接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。

输入格式
第一行包含两个整数 n 和 m。

接下来 n 行,每行包含两个整数 x 和 c。

再接下来 m 行,每行包含两个整数 l 和 r。

输出格式
共 m 行,每行输出一个询问中所求的区间内数字和。

数据范围
−10^9≤x≤10^9,
1≤n,m≤10^5,
−10^9≤l≤r≤10^9,
−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
0
5

思路:

/*离散化+前缀和*/ 
离散化其实就是一个映射的操作 我们看到的数轴上的数字无限多 我们根本没办法用数组下标全部表示 而给出的加c的数组却很有限只有1e5 那么我们可以只
记录这些数组但是...这些数组不是连续的而是离散化的 我们还算没办法用数组下标来表示 那么我们可先用一个容器vector存下这些出现的数组 那么数组下标就
映射到这些数字 也可以说数字映射到数组下标 这些下标一定是连续的 为了保证连续且单调 我们先用sort把数组排序 然后再去重 利用 
num.erase(unique(num.begin(),num.end()),num.end())实现去重操作 再来我们用一个数组a 其下标对应的就是vector里每一个数映射的下标类似下图
100000  <->1 <->a[1]
意思就是10000在vector里的下标为1 那么数组a[1]对应的就是10000的相关操作 每一次询问都是一个区间l,r我们可以利用find函数去寻找l映射的下标
r映射的下标然后计算 find是需要我们自己写的 实现思路就是一个二分查找
 

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int>P;
vector<int>num;//记录所有出过的数组 
vector<P>add,ask;//分布记录操作1(+c)的操作 和操作2(询问操作) 
const int N=300010;
int a[N],s[N];
int find(int x){
	int r=num.size()-1,l=0;
	while(l<r){
		int mid=(l+r)>>1;
		if(num[mid]>=x)r=mid;
		else l=mid+1;
	}
	return r+1;
}//二分查找每一个数映射的值 
int main(){
	int n,m,x,c;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>x>>c;
		add.push_back({x,c});
		num.push_back(x);
	}
	int l,r;
	for(int i=1;i<=m;i++){
		cin>>l>>r;
		ask.push_back({l,r});
		num.push_back(l);
		num.push_back(r);
	}//不仅需要将操作1的数放入num 操作2的数也需要 
	for(int i=0;i<add.size();i++){
		int pos=find(add[i].first);
		a[pos]+=add[i].second;
	}//每一个数映射到a数组下标存储其对应的值 
	for(int i=1;i<=num.size();i++)s[i]=s[i-1]+a[i];
	for(int i=0;i<m;i++){
		int tl=ask[i].first,tr=ask[i].second;
		cout<<s[tr]-s[tl-1]<<endl;
	}//前缀和 
}

金发姑娘和n头牛(acwing)

题源:ACwing
题目描述:
你可能听过关于金发姑娘和三只熊的经典故事。

然而,鲜为人知的是,金发姑娘最终成了一个农民。

在她的农场中,她的牛棚里有 N 头奶牛。

不幸的是,她的奶牛对温度相当敏感。

对于奶牛 i,使其感到舒适的温度为 Ai…Bi。

如果金发姑娘将牛棚的恒温器的温度 T 设置为 T<Ai,奶牛就会觉得冷,并会产出 X 单位的牛奶。

如果她将恒温器的温度 T 设置在 Ai≤T≤Bi,奶牛就会感到舒适,并会产出 Y 单位的牛奶。

如果她将恒温器的温度 T 设置为 T>Bi,奶牛就会觉得热,并会产出 Z 单位的牛奶。

正如所期望的那样,Y 的值始终大于 X 和 Z。

给定 X,Y,Z 以及每头奶牛感到舒适的温度范围,请计算通过合理设定恒温器温度,金发姑娘可获得的最大产奶量。

恒温器温度可设置为任何整数。

输入格式
第一行包含四个整数 N,X,Y,Z。

接下来 N 行,每行包含两个整数 Ai 和 Bi。

输出格式
输出可获得的最大产奶量。

数据范围
1≤N≤20000,
0≤X,Y,Z≤1000,
0≤Ai≤Bi≤109
输入样例:
4 7 9 6
5 8
3 4
13 20
7 10
输出样例:
31
样例解释
金发姑娘可以将恒温器温度设置为 7 或 8,这样会让奶牛 1 和 4 感到舒适,奶牛 2 感到热,奶牛 3 感到冷。

共可获得 31 单位牛奶。

思路:这个题目并不是去寻找被最佳温度区间覆盖的次数最多的温度 因为如果恰好右100个点x为0 y为500 z为460 而最佳温度点只被覆盖了2两次 其他点都是
处于低温度 那么这个一定不是最佳温度 所以我们差分要对于每一个牛来进行 也就是将温度轴分为三个区间 最低温~L-1 L~R R+1~最高温 如果那么很简单
让b[1]+=x b[L]-=x b[L]+=y b[R+1]-=y b[R+1]+=z b[num.size()]-=z b是差分数组 a是原数组 我们通过 a[i]=a[i-1]+b[i]就能算出最佳温度了 a[i]表示的是在i的温度下 所有牛的总产量当然因为数据范围太大离不开 离散操作

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll>P;
vector<ll>num;// 记录出现过的温度
vector<P>q;//记录询问的区间
const int N=40010;
ll x,y,z,n,a[N],b[N];
int find(ll x){
	int l=0,r=num.size()-1;
	while(l<r){
		int mid=(l+r)>>1;
		if(num[mid]>=x)r=mid;
		else l=mid+1;
	}
	return r+1;
}//通过二分查找找到每一个数对应的下标
int main(){
	cin>>n>>x>>y>>z;
	ll l,r;
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	for(int i=1;i<=n;i++){
		cin>>l>>r;
		q.push_back({l,r});
		num.push_back(l);
		num.push_back(r);
	}
	sort(num.begin(),num.end());
	num.erase(unique(num.begin(),num.end()),num.end());//去重+排序操作实现单调性
	for(int i=0;i<n;i++){
		int tl=find(q[i].first);
		int tr=find(q[i].second+1);
		b[1]+=x;
		b[tl]+=y-x;
		b[tr]+=z-y;
		b[num.size()]-=z;
	}//差分数组的计算
	ll ans=0,,sum=0;
	for(int i=1;i<=num.size();i++){
		sum+=b[i];
		ans=max(sum,ans);
	}//寻找最大产量的温度
	cout<<ans<<endl;
} 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值