Day7

离散化(Discretization)是算法竞赛中一个非常重要的技巧。它的核心思想是:当你在意的是数据之间的“相对大小(排名)”而不是“绝对数值”时,把大范围、稀疏的数据映射到小范围、紧凑的区间内。

以下从三个维度为你细讲:

1. 为什么要离散化?(痛点所在)

假设有一个问题:给你 \(n=10^5\) 个数,让你用树状数组统计一些信息。
如果这些数的大小范围是:

  • 情况 A: 数值在 \(1\)\(10^5\) 之间。直接开 int tree[100005],完美。
  • 情况 B: 数值在 \(1\)\(10^9\) 之间。你无法开出一个 int tree[1000000005] 的数组,内存会直接炸掉(MLE)。
  • 情况 C: 数值包含负数,如 \(-10^9\)\(10^9\)。数组下标不能为负,树状数组直接瘫痪。

矛盾点: 树状数组(和线段树)的性能和空间是随数值范围(值域)增长的,而我们的内存是有限的。
突破口: 只有 \(10^5\) 个数。虽然数值很大,但如果把它们按从小到大排个名,第 1 小、第 2 小……第 \(10^5\) 小。这个“排名”的范围只有 \([1, 10^5]\)

2. 离散化的核心逻辑

离散化就像是给这组数“发号码牌”

例子:
原数组 \(A = [100, 5, 20000, 5, 999999]\)

  1. 去重排序:得到有序序列 \(L = [5, 100, 20000, 999999]\)
  2. 映射(发牌)
    • 数值 \(5\) \(\to\) 排名 \(1\)
    • 数值 \(100\) \(\to\) 排名 \(2\)
    • 数值 \(20000\) \(\to\) 排名 \(3\)
    • 数值 \(999999\) \(\to\) 排名 \(4\)
  3. 替换:原数组 \(A\) 变成了 \(A' = [2, 1, 3, 1, 4]\)

观察: \(A'\) 中数字的相对大小关系和 \(A\) 完全一致(比如 \(A[0] > A[1]\),对应的 \(A'[0] > A'[1]\) 依然成立)。现在我们只需要一个大小为 \(4\) 的树状数组就能处理原本需要 \(10^6\) 空间的逻辑。

3. 代码实现细节

在你提供的代码中:

for(int i=1;i<=n;i++) scanf("%lld",&a[i]), lsh[i] = a[i]; // 1. 复制一份到离散化数组
sort(lsh+1, lsh+n+1);                                     // 2. 排序
// unique(lsh+1, lsh+n+1);                                // (可选) 去重
for(int i=1;i<=n;i++) 
    a[i] = lower_bound(lsh+1, lsh+n+1, a[i]) - lsh;       // 3. 查找排名并替换
  • sort:确定谁大谁小。
  • unique:如果你希望相同的数字对应同一个排名,就需要去重。在求逆序对时,如果不对相同元素去重,lower_bound 也会让相同的数找到相同的排名(第一个出现的位置),逻辑上也是通的。
  • lower_bound:这是在有序数组中找“排名”的最快方法(二分查找),复杂度 \(O(\log n)\)

4. 什么时候需要离散化?

当你看到以下特征时,就要考虑离散化:

  1. 空间限制:数组下标需要用到题目给出的数值,但数值范围(如 \(10^9\))远大于数组能开的大小。
  2. 性质只关乎大小:题目只关心 \(a_i\) 是否大于 \(a_j\),或者关心 \(a_i\) 在所有数中的排名(如:逆序对、求第 \(K\) 大、统计比当前数小的数有多少个)。
  3. 静态数据:离散化通常针对初始就给定的数据。如果是动态增加全新的、范围极大的数值,离散化就比较难做,通常需要改用动态开点线段树或平衡树。

总结

离散化就是把“稀疏的大值域”压缩成“连续的紧凑值域”。它是为了在不改变元素相对关系的前提下,让数据能够适配那些“以数值为下标”的数据结构(如树状数组)。

include<bits/stdc++.h>

define lowbit(x) x & (-x)

define int long long

using namespace std;
const int maxn = 5e5 + 10;
int n,a[maxn],sum[maxn],lsh[maxn];
void add(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
sum[i] += y;
return ;
}
int query(int x)
{
int ans = 0;
for(int i=x;i>=1;i-=lowbit(i))
ans += sum[i];
return ans;
}
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),lsh[i] = a[i];
sort(lsh+1,lsh+n+1);
//unique(lsh+1,lsh+n+1);//如有需要可判重
for(int i=1;i<=n;i++) a[i] = lower_bound(lsh+1,lsh+n+1,a[i])-lsh;
int ans = 0;
for(int i=1;i<=n;i++)
{
add(a[i],1);
ans += i-query(a[i]);
}
printf("%lld\n",ans);
return 0;
}根据我的代码改

posted @ 2026-01-28 10:59  EcSilvia  阅读(0)  评论(0)    收藏  举报