目 录CONTENT

文章目录

离散化

千年的霜雪
2024-03-01 / 0 评论 / 1 点赞 / 9 阅读 / 0 字 / 正在检测是否收录...

离散化

集训的时候,离散化这块明明很简单,但始终听不懂。现在回头学了第二遍,终于懂了,于是记录一下。

思路

由于离散化后会破坏数之间的具体大小关系,所以只有在只需要数的相对大小关系时可以离散化,同时意味着我们可以对原数组排序。

  1. 去重
  2. 查找原数组中某个数对应的,离散化后的数的位置

这里第二部查找时,由于排序后的数组有序,可以用二分来解决。

一般情况下,离散化只存在从原数组寻找离散化后的数组的操作,不存在反向搜索。
所以,在离散化后的任何操作,例如求前缀和或差分,都需要转化为对离散化后的数组的操作。
即原数组一般情况下已不可操作!!!

模板

vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end());   // 去掉重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于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, 2, ...n ;+1从1开始映射;不+1从0开始映射
}

题目

火烧赤壁(洛谷P1496)

题目背景

曹操平定北方以后,公元 208 年,率领大军南下,进攻刘表。他的人马还没有到荆州,刘表已经病死。他的儿子刘琮听到曹军声势浩大,吓破了胆,先派人求降了。

孙权任命周瑜为都督,拨给他三万水军,叫他同刘备协力抵抗曹操。

隆冬的十一月,天气突然回暖,刮起了东南风。

没想到东吴船队离开北岸大约二里距离,前面十条大船突然同时起火。火借风势,风助火威。十条火船,好比十条火龙一样,闯进曹军水寨。那里的船舰,都挤在一起,又躲不开,很快地都烧起来。一眨眼工夫,已经烧成一片火海。

曹操气急败坏的把你找来,要你钻入火海把连环线上着火的船只的长度统计出来!

题目描述

给定每个起火部分的起点和终点,请你求出燃烧位置的长度之和。

输入格式

第一行一个整数,表示起火的信息条数 n
接下来 n 行,每行两个整数 a, b,表示一个着火位置的起点和终点(注意:左闭右开)。

输出格式

输出一行一个整数表示答案。

样例 #1

样例输入 #1
3
-1 1
5 11
2 9
样例输出 #1
11

提示

数据规模与约定

对于全部的测试点,保证 1 \leq n \leq 2 \times 10^4-2^{31} \leq a \leq b \lt 2^{31},且答案小于 2^{31}

思路

首先,此题可以使用差分解决,即将每次给的范围内的数的值都加1,最后统计值不是0的数的个数。
但是,由于此题 a、b 的范围过大,不能直接使用差分,否则会开的数组过大,同时数的总个数又不算太多,这时就需要使用离散化。

同时注意此题与原思路稍不同的地方:
由于这里我们离散化时,是将原数组排序并去重后,直接将处理后的数组的下标作为了离散化后的值,则此时我们就可以通过下标,反向找到离散化前的原值。这也是我之前理解不了的地方。

代码参考

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <stdio.h>
#include <vector>
#include <algorithm>

using namespace std;

const int MAX = 2 * 2e4 + 10;//每组两个值,所以要开两倍

typedef long long ull;

typedef pair<ull, ull> PII;

vector<ull> alls;//待离散化数组
vector<PII> quest;//成对存储原数组 - 用于差分

int b[MAX];//差分数组
int a[MAX];//差分求和后的数组

int num;//离散化后的数组的总长
ull ans;

int find(int x)
{
	int l = 0, r = num - 1;
	while (l < r)
	{
		int mid = l + r >> 1;
		if (alls[mid] >= x) r = mid;
		else l = mid + 1;
	}
	return r + 1;
}//find函数用来查找原数对应的离散化后的数

int main()
{
	int n = 0;
	cin >> n;

	for(int i = 0; i < n; i++)
	{
		ull l = 0, r = 0;
		scanf("%lld %lld", &l, &r);
		alls.push_back(l);
		alls.push_back(r);
		quest.push_back({ l, r });
	}//读入 - 将左右端点都存入待离散化的数组,同时再分别存起来,方便之后处理

	sort(alls.begin(), alls.end());//排序
	alls.erase(unique(alls.begin(), alls.end()), alls.end());//去重
	num = alls.size();//求一下alls数组总长,避免多次重复求长

	for (int i = 0; i < n; i++)
	{
		b[find(quest[i].first)] += 1;
		b[find(quest[i].second)] -= 1;
	}//差分

	for (int i = 1; i <= num; i++) a[i] = a[i - 1] + b[i];//差分求和

	int l = 0, r = 0; 
	for (int i = 1; i < num; i++)
	{
		if (a[i] && a[i - 1] == 0) l = i;
		if (a[i] && a[i + 1] == 0) r = i + 1;
		if (r > l)
		{
			ans += alls[r - 1] - alls[l - 1];//差分数组从1开始,但原数组对应从0开始,所以这里求原数组时要-1
			l = r;
		}
	}//将差分后的数组对应至原数组,统计不是0的长度,l、r为左右端点

	cout << ans << endl;
	return 0;
}
1

评论区