离散化
离散化
赶紧胡一篇出来,免得到时候出锅。
毕竟离散化是权值线段树学习的基础。
定义
把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。
\(\qquad \qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\) ————百度百科
原理
简单来说,给出 \(n(n \le 10^5)\) 个数字 \(a_i(a_i \le 10^9)\),如果在这时建立权值线段树很明显会 \(MLE\),所以将其离散化,也就是表示为这些数字的相对大小。
比如一个数组 \(\left\{ 1,34,6,14,8 \right\}\)
根据数字之间相对大小关系,离散化之后就是:
实现
实现的过程要用到 \(STL\) 库中的 \(lower bound\) 这个好东西。
\(lower bound\) 的用处是:在给定的有序序列中找到第一个小于给定数的数的下标,这也就很好的起到了离散化的作用。
至于代码的实现,就是把所有数先全存在一个数组中,每次对于要提取的数,用 \(lower bound\) 即可。
n=R();
for(int i=1;i<=n;i++){
a[i]=R(),t[i]=a[i];
}sort(t+1,t+1+n);
m=unique(t+1,t+1+n)-t-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(t+1,t+m+1,a[i])-t;
}
例题
给定一个白色数轴,每次将给定区间 \(\left[ a_i,b_i \right]\) 染成白色。多次染色效果相同 ,求最后染色区间总长度。
如果在 \(a_i,b_i\) 都很小的情况下,就可以用一个数组表示整个数轴,数组中的元素 \(f_i\) 表示的是长度为 \(1\) 的区间 \(\left[ i,i+1 \right]\) 是否被染色,然后模拟每个指令,枚举数组区间并对数组 \(f\) 赋值即可。
因为 \(a_i,b_i\) 均很大,所以要用到离散化。
因为题目中有用的数字不多,所以两个端点内点统一处理即可。
比如区间 \(\left[ -1,1 \right]\) 转化为 \(\left[ 1,2 \right]\) 染色,最后统计答案是将区间 \(\left[ 1,2 \right]\) 的长度记录为 \(2\) 即可。
#include <bits/stdc++.h>
using namespace std;int R(){
int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);return x;
}void W(int x){if(x>9)W(x/10);putchar(x%10+'0');}const int N=2e4+10;
int n,a[N],b[N],f[N<<1],dt,ct,d[N<<1],x,y;long long c[N<<1],ans;
int main(){
n=R();for(int i=1;i<=n;i++)a[i]=R(),b[i]=R(),d[++dt]=a[i],d[++dt]=b[i];sort(d+1,d+1+dt);
for(int i=1;i<=dt;i++)if(d[i]!=d[i-1]||i==1)c[++ct]=d[i];
for(int i=1;i<=n;i++){
x=lower_bound(c+1,c+ct+1,a[i])-c;
y=lower_bound(c+1,c+ct+1,b[i])-c;
for(int j=x;j<y;j++)f[j]=1;
}for(int i=1;i<ct;i++)if(f[i])ans+=c[i+1]-c[i];cout<<ans<<endl;return 0;
}