离散化——Acwing.802区间和

2024-06-13 22:44
文章标签 区间 离散 acwing.802

本文主要是介绍离散化——Acwing.802区间和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

离散化

定义

离散化可以简单理解为将连续的数值或数据转换为离散的、有限个不同的值或类别。离散化就是将一个可能具有无限多个取值或在一个较大范围内连续取值的变量,通过某种规则或方法,划分成若干个离散的区间或类别,并将原始数据映射到这些离散的类别中。

主要目的通常是为了简化数据处理、降低数据维度、提高计算效率或适应特定的算法和模型要求。离散化可以去除一些不太重要的细节信息,突出数据的主要特征和模式。

运用情况

当数据范围很大,但实际涉及到的不同值相对较少时,通过离散化可以用较小的索引或标记来代表原来的数值,从而节省空间和提高处理效率。

例如,有一组数值可能非常大(比如从 1 到 1000000),但实际上只有少数几种不同的值频繁出现,通过离散化可以将这些值映射到一个较小的范围内(比如 1 到 10)。

离散化的一个常见做法是先对原始数据进行排序,然后为每个不同的值分配一个唯一的编号或索引。这样在后续的处理中就可以用这些编号来代替具体的数值进行操作。

离散化在一些算法中,如一些基于区间或计数的问题中经常被用到,可以使问题的处理更加简洁和高效。

注意事项

  1. 要确保离散化后的数据能准确反映原始数据的关键特征和关系,不能丢失重要信息。
  2. 注意边界情况的处理,避免出现遗漏或错误分类。
  3. 对于不同的应用场景,选择合适的离散化方法,考虑数据分布特点和后续算法的需求。

离散方法

  1. 等宽离散化:将数据的取值范围划分为若干个等宽的区间,每个区间对应一个离散值。
  2. 等频离散化:将数据划分为若干个区间,使得每个区间内的数据数量大致相等。
  3. 基于聚类的离散化:利用聚类算法将数据聚成若干类,然后将这些类作为离散化后的类别。
  4. 基于特定规则的离散化:根据具体问题和业务需求,人为设定一些规则来进行离散化,比如根据某个阈值进行划分。
  5. 基于决策树的离散化:通过构建决策树,根据节点分裂的情况来确定离散化的划分点。

解题思路

  1. 分析问题:确定是否适合使用离散化,以及离散化的目的是什么。
  2. 选择方法:根据数据特点和问题需求选择合适的离散化方法,如等宽、等频等。
  3. 处理数据:按照选定的方法对数据进行离散化操作,建立映射关系。
  4. 验证效果:检查离散化后的数据在后续处理或分析中是否达到预期效果,必要时进行调整。
  5. 结合算法:考虑离散化后如何与具体的算法或模型相结合,使其更好地发挥作用。

例如,在处理一些区间相关的问题时,可能先对区间的端点进行离散化,然后根据离散化后的索引进行计算和处理;或者在数据量很大但实际不同值有限的情况下,通过离散化来提高存储和计算效率。同时,在解题过程中要不断思考如何优化离散化的过程以及更好地利用离散化后的结果。

Acwing.802区间和

题目描述

802. 区间和 - AcWing题库

运行代码

#include<bits/stdc++.h>  
using namespace std;  
typedef pair<int, int> PII;  
const int N = 300010;  
PII add[N];  
PII query[N];  
int a[N], s[N];  
vector<int> alls;  
int find(int x) {  return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;  
}  int main() {  int n, m;  cin >> n >> m;  // 读取修改操作并存储到alls中  for (int i = 0; i < n; i++) {  cin >> add[i].first >> add[i].second;  alls.push_back(add[i].first);  }  // 读取查询操作并存储到alls中  for (int i = 0; i < m; i++) {  cin >> query[i].first >> query[i].second;  alls.push_back(query[i].first);  alls.push_back(query[i].second);  }  // 离散化  sort(alls.begin(), alls.end());  alls.erase(unique(alls.begin(), alls.end()), alls.end());  // 根据离散化后的位置更新a数组  for (int i = 0; i < n; i++) {  int pos = find(add[i].first);  a[pos] += add[i].second;  }  // 计算前缀和  for (int i = 1; i <= alls.size(); i++) {  s[i] = s[i - 1] + a[i];  }  // 处理查询  for (int i = 0; i < m; i++) {  int l = find(query[i].first);  int r = find(query[i].second);  cout << s[r] - s[l - 1] << endl;  }  return 0;  
}

代码思路

  1. 读取输入
    • 读取修改操作的个数 n 和查询操作的个数 m
    • 读取每个修改操作,包括修改的位置 x 和增加的值 c,并将位置 x 存储在 add 数组中。
    • 读取每个查询操作,包括查询的左边界 l 和右边界 r,并将它们存储在 query 数组中。
  2. 离散化:将所有修改和查询中涉及到的位置(即 add 和 query 数组中的 xl 和 r)存储到 alls 向量中。对 alls 向量进行排序,并使用 unique 函数去除重复元素,实现离散化。
  3. 更新数组:遍历 add 数组,对于每个修改操作,使用 find 函数找到离散化后的位置 pos,并更新 a[pos] 数组的值。
  4. 计算前缀和:遍历离散化后的位置数组 alls(从索引 1 开始,因为索引 0 不代表任何位置),计算 a 数组的前缀和,并将结果存储在 s 数组中。
  5. 处理查询
    • 遍历 query 数组,对于每个查询操作,使用 find 函数找到离散化后的左边界 l 和右边界 r 的位置。
    • 输出区间和 s[r] - s[l - 1],其中 s[r] 是右边界的前缀和,s[l - 1] 是左边界左边一个位置的前缀和(用于排除左边界本身)。
  6. 输出结果:对于每个查询操作,输出计算得到的区间和。

这个代码的时间复杂度主要由离散化、更新数组、计算前缀和以及处理查询四部分组成。其中,离散化和计算前缀和的时间复杂度都是 O(n log n),其中 n 是修改和查询操作的总数(因为 alls 的大小可能达到这个数量级)。更新数组和处理查询的时间复杂度都是 O(n),所以整体的时间复杂度是 O(n log n)。

改进思路

  1. 减少alls数组中的冗余元素:在将位置加入alls数组时,如果两个修改或查询操作的位置相同,我们只需要存储一个。这可以通过在加入新位置之前检查alls数组的末尾元素是否与新位置相同来实现。

  2. 使用std::vectorback_inserter:在将位置加入alls数组时,使用std::back_inserter可以避免显式地调用push_back,这可能会稍微提高性能。

  3. 使用std::lower_bound的返回值直接作为索引:在find函数中,我们可以直接使用std::lower_bound返回的迭代器减去alls.begin()得到离散化后的索引,而不是将其转换为整数。

改进代码(AI)

#include<bits/stdc++.h>  
using namespace std;  
typedef pair<int, int> PII;  
const int N = 300010;  PII add[N];  
PII query[N];  
int a[N], s[N];  
vector<int> alls;  int find(int x) {  return lower_bound(alls.begin(), alls.end(), x) - alls.begin() + 1;  
}  int main() {  int n, m;  cin >> n >> m;  // 读取修改操作  for (int i = 0; i < n; i++) {  cin >> add[i].first >> add[i].second;  if (alls.empty() || alls.back() != add[i].first) alls.push_back(add[i].first);  }  // 读取查询操作  for (int i = 0; i < m; i++) {  cin >> query[i].first >> query[i].second;  if (alls.empty() || alls.back() != query[i].first) alls.push_back(query[i].first);  if (alls.empty() || alls.back() != query[i].second) alls.push_back(query[i].second);  }  // 离散化  sort(alls.begin(), alls.end());  alls.erase(unique(alls.begin(), alls.end()), alls.end());  // 根据离散化后的位置更新a数组  for (int i = 0; i < n; i++) {  int pos = find(add[i].first);  a[pos] += add[i].second;  }  // 计算前缀和  for (int i = 1; i <= alls.size(); i++) {  s[i] = s[i - 1] + a[i];  }  // 处理查询  for (int i = 0; i < m; i++) {  int l = find(query[i].first);  int r = find(query[i].second);  cout << s[r] - s[l - 1] << endl;  }  return 0;  
}

这篇关于离散化——Acwing.802区间和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1058632

相关文章

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu4267区间统计

题意:给一些数,有两种操作,一种是在[a,b] 区间内,对(i - a)% k == 0 的加value,另一种操作是询问某个位置的值。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import

hdu4417区间统计

给你一个数列{An},然后有m次查询,每次查询一段区间 [l,r] <= h 的值的个数。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamRead

hdu3333区间统计

题目大意:求一个区间内不重复数字的和,例如1 1 1 3,区间[1,4]的和为4。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;

【uva】11536-Smallest Sub-Array(区间移动问题)

一个区间移动的问题,1A了,感觉没什么好说的。。 13975926 11536 Smallest Sub-Array Accepted C++ 0.809 2014-08-01 11:00:20 #include<cstdio>#include<cstring>#include<iostream>using namespace std;#define INF 1 << 30

【hdu】Just a Hook(线段树区间修改)

线段树模板题,练的是懒惰标记。 懒惰标记,就是更新一段区间的时候,如果小区间被包含在了所需要更新的区间里面,那么直接对代表这个区间的数组元素赋值,之后做一个标记(表示这个区间的子区间都需要更新)但是不继续递归(这样可以节省很多的时候)。 116571152014-09-15 14:17:26Accepted1698796MS2380K1750 BG++KinderRiven #

【hdu】I Hate It(线段树,结点修改求区间最大值)

线段树的模板题,还是二分递归。 #include <iostream>#include <cstdlib>#include <cstdio>#include <string>#include <cstring>#include <cmath>#include <vector>#include <queue>#include <set>#include <map>#incl

【hdu】敌兵布阵(线段树,更加结点,区间求和)

最近开始刷线段树,主要围绕notonlysuccess的线段树总结刷。 结点修改还是比较简单的,不需要什么懒惰标记,直接二分递归就可以了。 #include <iostream>#include <cstdlib>#include <cstdio>#include <string>#include <cstring>#include <cmath>#include <vecto

ZOJ 3324 Machine(线段树区间合并)

这道题网上很多代码是错误的,由于后台数据水,他们可以AC。 比如这组数据 10 3 p 0 9 r 0 5 r 6 9 输出应该是 0 1 1 所以有的人直接记录该区间是否被覆盖过的方法是错误的 正确方法应该是记录这段区间的最小高度(就是最接近初始位置的高度),和最小高度对应的最长左区间和右区间 开一个sum记录这段区间最小高度的块数,min_v 记录该区间最小高度 cover

【Leetcode56】合并区间(数组 | 排序)

文章目录 一、题目二、思路三、代码 一、题目 二、思路 先将所有子列表按照start_pos进行排序,有利于保持顺序性,每次处理新子列表时,只用和结果列表ans_lst的最后一个子列表对比,如果有重合则合并,然后将合并的新子列表插入结果列表排序可以使用lambda函数,intervals.sort(key=lambda x: x[0])因为使用了sort,所以时间复杂度O(