【一百零九】【算法分析与设计】树状数组求解前缀最大值,673. 最长递增子序列的个数,树状数组求前缀区间最大值

news2024/12/27 11:52:12

树状数组求解前缀最大值

树状数组可以求解和前缀区间有关的问题,例如前缀和,前缀区间最值.
可以利用 l o g n log_n logn的时间复杂度快速查找前缀信息.
利用树状数组查询前缀区间中最大值问题.
在这里插入图片描述
树状数组下标1位置存储arr数组下标1位置的最大值.
树状数组2位置存储arr数组1,2位置最大值.
数组数组3位置存储arr数组3位置最大值.
以此类推…
查找arr数组1~i前缀区间最大值.利用lowbit跳转,记录遍历的每一个tree[i]的最大值.

lowbit跳转,while(i<tree.size()),i+=lowbit(i),i会遍历所有拥有i位置的数组数组位置.
while(i>0)i-=lowbit(i),i会遍历所有能够组成1,2,3…i的树状数组位置.
在这里插入图片描述
如果i位置是7,lowbit往前跳转,树状数组会遍历4,6,7位置,对应arr组成1,2,3,4,5,6,7.
往后跳转,树状数组会遍历7,8,拥有arr中7位置的树状数组位置.

#include<bits/stdc++.h>
using namespace std;

#define int long long // 定义 int 为 long long 类型,方便处理大数
#define endl '\n' // 定义 endl 为换行符,方便输出

vector<int> arr = { 1, 4, 2, 5, 3, 5, 6, 3, 2, 5 }; // 定义一个全局数组并初始化

// 树状数组类定义
class Tree {
public:
    vector<int> tree; // 定义一个向量 tree,用于存储树状数组

    // 计算 lowbit
    int lowbit(int i) {
        return i & -i; // 返回 i 和 -i 的按位与,获取最低位的 1
    }

    // 在位置 i 处更新值 v
    void sett(int i, int v) {
        while (i <= tree.size()) { // 从索引 i 开始,向上更新树状数组
            tree[i] = max(tree[i], v); // 更新节点为当前值和新值的最大值
            i += lowbit(i); // 移动到下一个需要更新的位置
        }
    }

    // 查询前缀 [1, i] 的最大值
    int gett(int i) {
        int ret = LLONG_MIN; // 初始化结果为负无穷大
        while (i > 0) { // 从索引 i 开始,向下计算前缀最大值
            ret = max(tree[i], ret); // 更新结果为当前值和当前结果的最大值
            i -= lowbit(i); // 移动到下一个需要计算的位置
        }
        return ret; // 返回前缀最大值
    }

    // 默认构造函数
    Tree() {}

    // 使用给定大小 n 初始化树状数组
    Tree(int n) {
        tree.assign(n + 3, 0); // 初始化树状数组大小,并将值设为 0
    }

    // 使用给定数组初始化树状数组
    Tree(vector<int> arr) {
        tree.assign(arr.size() + 3, LLONG_MIN); // 初始化树状数组大小,并将值设为负无穷大
        int i = 1;
        for (auto& xx : arr) {
            sett(i++, xx); // 将数组中的值添加到树状数组中
        }
    }
};

Tree t1(arr); // 使用全局数组初始化树状数组 t1
int q; // 定义查询次数

// 主解题函数
void solve() {
    for (auto& xx : arr) {
        cout << xx << " "; // 输出数组中的每个元素
    }
    cout << endl;
    for (int i = 0; i < arr.size(); i++) {
        cout << i << " "; // 输出数组的索引
    }
    cout << endl;
    cin >> q; // 读取查询次数
    for (int i = 1; i <= q; i++) {
        int k;
        cin >> k; // 读取查询索引
        k++;
        cout << t1.gett(k) << endl; // 输出前缀 [1, k] 的最大值
    }
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 快速输入输出

    solve(); // 调用解题函数
}

673. 最长递增子序列的个数

给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。

注意 这个数列必须是 严格 递增的。

示例 1:

输入: [1,3,5,4,7]
输出: 2
解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。
示例 2:

输入: [2,2,2,2,2]
输出: 5
解释: 最长递增子序列的长度是1,并且存在5个子序列的长度为1,因此输出5。

提示:

1 < = n u m s . l e n g t h < = 2000 1 <= nums.length <= 2000 1<=nums.length<=2000
− 1 0 6 < = n u m s [ i ] < = 1 0 6 -10^6 <= nums[i] <= 10^6 106<=nums[i]<=106

countt数组,对应arr数组每一个元素.
countt存储node类型,node类型有两个变量,maxlen和count.
countt[i].maxlen表示arr数组中以i位置元素为结尾的最长递增子序列的长度,countt[i].count表示arr数组中以i位置元素为结尾的最长递增子序列的个数.
如果countt数组维护完,只需要直到最长递增子序列的长度,然后遍历countt数组,统计是最长递增子序列长度的个数即可.

填写countt[i]位置的数据,需要直到1~i-1区间中,元素值小于arr[i]的最长递增子序列的长度,以及个数.
元素值小于arr[i],对应词频表,元素值映射个数,词频.元素值作为下标,刚好是排序好的,也就是求前缀和.
1~i-1,对应依次遍历.
利用树状数组存储以某元素值为结尾的最长递增子序列的长度,元素值映射最长的长度.
在这里插入图片描述
index映射arr,元素值映射index,index映射maxlen.

利用map做离散化处理.
利用树状数组查询1~i-1以小于arr[i]元素结尾的最长递增子序列的长度.
然后遍历arr数组,统计数量.维护当前位置countt.

Tree中gett函数获取1~i最大值,但是如果进来的i是0,直接返回0,最大长度不存在.因为外面要用这个最大长度+1表示当前的最大长度.

struct node {
    int maxlen = 1, count = 0; // 定义节点结构,包含最大长度和计数
};

class Tree {
public:
    vector<int> tree; // 定义一个向量 tree,用于存储树状数组

    // 计算 lowbit
    int lowbit(int i) { 
        return i & -i; // 返回 i 和 -i 的按位与,获取最低位的 1
    }

    // 在位置 i 处更新值 v
    void sett(int i, int v) {
        while (i < tree.size()) { // 从索引 i 开始,向上更新树状数组
            tree[i] = max(tree[i], v); // 更新节点为当前值和新值的最大值
            i += lowbit(i); // 移动到下一个需要更新的位置
        }
    }

    // 查询前缀 [1, i] 的最大值
    int gett(int i) {
        int ret = INT_MIN; // 初始化结果为负无穷大
        if (i == 0) // 如果索引为 0
            return 0; // 返回 0
        while (i > 0) { // 从索引 i 开始,向下计算前缀最大值
            ret = max(ret, tree[i]); // 更新结果为当前值和当前结果的最大值
            i -= lowbit(i); // 移动到下一个需要计算的位置
        }
        return ret; // 返回前缀最大值
    }

    // 默认构造函数
    Tree() {}
};

class Solution {
public:
    Tree t1; // 定义一个树状数组对象 t1
    vector<int> arr; // 定义一个向量 arr,用于存储输入的数组
    map<int, int> arr_index; // 定义一个 map,用于存储数组元素的索引
    vector<node> countt; // 定义一个向量 countt,用于存储每个位置的节点信息
    int ret = 0; // 定义一个变量 ret,用于存储结果
    int maxnlen = 0; // 定义一个变量 maxnlen,用于存储最大长度

    // 主解题函数
    void solve() {
        maxnlen = 0; // 初始化最大长度为 0
        ret = 0; // 初始化结果为 0
        arr_index.clear(); // 清空索引 map
        for (auto& x : arr) { // 遍历输入数组
            arr_index[x]; // 在 map 中记录每个元素
        }
        int index = 1;
        for (auto& x : arr_index) { // 给每个元素分配唯一的索引
            x.second = index++;
        }
        countt.assign(arr.size(), node()); // 初始化 countt 向量
        t1.tree.assign(index, 0); // 初始化树状数组
        for (int i = 0; i < arr.size(); i++) { // 遍历数组
            int index = arr_index[arr[i]]; // 获取当前元素的索引
            int maxlen = t1.gett(index - 1); // 获取当前索引之前的最大长度
            int ans = 0;
            for (int j = 0; j < i; j++) { // 遍历之前的元素
                if (countt[j].maxlen == maxlen && arr[j] < arr[i]) { // 找到符合条件的元素
                    ans += countt[j].count; // 累加计数
                }
            }
            countt[i].maxlen = maxlen + 1; // 更新当前元素的最大长度
            countt[i].count = max(ans, 1); // 更新当前元素的计数
            t1.sett(index, countt[i].maxlen); // 在树状数组中更新当前元素的最大长度
            maxnlen = max(maxnlen, countt[i].maxlen); // 更新最大长度
        }

        for (int i = 0; i < countt.size(); i++) { // 遍历 countt 向量
            if (countt[i].maxlen == maxnlen) { // 找到最大长度的元素
                ret += countt[i].count; // 累加结果
            }
        }
    }

    // 查找最长递增子序列的数量
    int findNumberOfLIS(vector<int>& _nums) {
        ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 快速输入输出
        arr = _nums; // 将输入数组赋值给 arr
        solve(); // 调用解题函数
        return ret; // 返回结果
    }
};


结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1800902.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

树的重心-java

主要通过深度优先搜索来完成树的重心&#xff0c;其中关于树的重心的定义可以结合文字多加理解。 文章目录 前言☀ 一、树的重心☀ 二、算法思路☀ 1.图用邻接表存储 2.图的遍历 3.算法思路 二、代码如下☀ 1.代码如下&#xff1a; 2.读入数据 3,代码运行结果 总结 前言☀ 主…

《PyTorch 实战宝典》重磅发布!

Pytorch 是目前常用的深度学习框架之一&#xff0c;比起 TF 的框架环境配置不兼容&#xff0c;和 Keras 由于高度封装造成的不灵活&#xff0c;PyTorch 无论是在学术圈还是工业界&#xff0c;都相当占优势。 不夸张地说&#xff0c;掌握了 PyTorch &#xff0c;就相当于走上了…

Cloudpods 强大的多云管理平台部署

简介 Cloudpods 是一款简单、可靠的企业IaaS资源管理软件。帮助未云化企业全面云化IDC物理资源&#xff0c;提升企业IT管理效率。 Cloudpods 帮助客户在一个地方管理所有云计算资源。统一管理异构IT基础设施资源&#xff0c;极大简化多云架构复杂度和难度&#xff0c;帮助企业…

[ue5]建模场景学习笔记(5)——必修内容可交互的地形,交互沙(2)

1需求分析&#xff1a; 继续制作可交互沙子内容&#xff0c;前面我们已经让角色在指定区域留下痕迹&#xff0c;那么能否让区域移动起来&#xff0c;这样才能逐步满足角色走到哪里都能产生交互痕迹&#xff0c;满足更大的地图。 2.操作实现&#xff1a; 1.首先建立角色能产生…

12、SpringBoot 源码分析 - 自动配置深度分析五

SpringBoot 源码分析 - 自动配置深度分析五 refresh和自动配置大致流程OnClassCondition的createOutcomesResolver创建结果解析器StandardOutcomesResolver的resolveOutcomes解析结果StandardOutcomesResolver的getOutcomeClassNameFilter的MISSING判断是否没有 ThreadedOutcom…

【YOLOv5/v7改进系列】改进池化层为SPP、SPPF、SPPCSPC

一、导言 池化层&#xff08;Pooling Layer&#xff09;是卷积神经网络&#xff08;Convolutional Neural Networks, CNNs&#xff09;中的一个重要组成部分&#xff0c;主要用于减少输入数据的空间尺寸&#xff08;例如&#xff0c;图像的宽度和高度&#xff09;&#xff0c;…

3D打印随形透气钢:技术革新引领模具制造新潮流

在模具制造领域&#xff0c;透气钢一直扮演着重要角色&#xff0c;它能够有效解决模具困气问题&#xff0c;提高注塑成型的效率和质量。然而&#xff0c;传统的透气钢制造方法受限于工艺和材料&#xff0c;难以满足复杂模具的需求。随着3D打印技术的飞速发展&#xff0c;3D打印…

kettle从入门到精通 第六十四课 ETL之kettle kettle中执行SQL脚本步骤,使用需当心

想真正学习或者提升自己的ETL领域知识的朋友欢迎进群&#xff0c;一起学习&#xff0c;共同进步。文章底部关注我&#xff0c;公众号后台加我微信入群&#xff0c;备注kettle。 1、群里有不定时会有同学反馈执行SQL脚本步骤使用有问题&#xff0c;那么咱们今天一起来学习下该步…

【数据分析基础】实验一 Python运算符、内置函数、序列基本用法

一、实验目的 熟练运用Python运算符。熟练运用Python内置函数。掌握Python的基本输入输出方法。了解lambda表达式作为函数参数的用法。掌握列表、元组、字典、集合的概念和基本用法。了解Python函数式编程模式。 二、实验内容&#xff1a; 1. 在命令模式测试如下命令&#x…

AI-知识库搭建(一)腾讯云向量数据库使用

一、AI知识库 将已知的问答知识&#xff0c;问题和答案转变成向量存储在向量数据库&#xff0c;在查找答案时&#xff0c;输入问题&#xff0c;将问题向量化&#xff0c;匹配向量库的问题&#xff0c;将向量相似度最高的问题筛选出来&#xff0c;将答案提交。 二、腾讯云向量数…

【C++题解】1261. 韩信点兵

问题&#xff1a;1261. 韩信点兵 类型&#xff1a; 题目描述&#xff1a; 韩信有一对士兵&#xff0c;他想知道有多少人&#xff0c;他就让士兵报数&#xff0c;如果按照 1 到 5 报数&#xff0c;最末一个士兵报的数为 1 。 按照 1 到 6 报数&#xff0c;最末一个士兵报的数为…

Oracle EBS AP发票创建会计科目错误:子分类帐日记帐分录未按输入币种进行平衡

系统版本 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.6 问题症状: 提交“创建会计科目”请求提示错误信息如下: 中文报错: 该子分类帐日记帐分录未按输入币种进行平衡。请检查日记帐分录行中输入的金额。 英文报错:The subledger journal entry does not balance i…

【Stable Diffusion】(基础篇二)—— Stable Diffusion图形界面介绍和基本使用流程

本系列笔记主要参考B站nenly同学的视频教程&#xff0c;传送门&#xff1a;B站第一套系统的AI绘画课&#xff01;零基础学会Stable Diffusion&#xff0c;这绝对是你看过的最容易上手的AI绘画教程 | SD WebUI 保姆级攻略_哔哩哔哩_bilibili 在上一篇博客中&#xff0c;我们成功…

数字科技如何助力博物馆设计,强化文物故事表现力?

国际博物馆日是每年为了推广博物馆和文化遗产&#xff0c;而设立的一个特殊的日子&#xff0c;让我们可以深入探讨博物馆如何更好地呈现和保护我们的文化遗产&#xff0c;随着近年来的数字科技发展&#xff0c;其在博物馆领域的应用越来越广泛&#xff0c;它为博物馆提供了新的…

把qml程序制作成安装包(Windows)

先检查一下有没有安装Qt Installer FrameWork 需要用到Qt自带的打包工具&#xff1a; Qt Installer FrameWork&#xff0c;虽然有点拉胯&#xff0c;但是也能用用。一般放在Qt目录下的Tools文件夹下&#xff0c;如果没有看到&#xff0c;就去在线下载器去下载一下。 步骤1 随…

如何在没有密码的情况下解锁iPhone

通常&#xff0c;您可以使用密码、FaceID 或 Touch ID 轻松解锁 iPhone。但是&#xff0c;有时您可能会忘记密码、iPhone 已停用或您的二手手机已锁定。在这种情况下&#xff0c;您必须绕过 iPhone 密码才能访问您的设备。在本文中&#xff0c;我们将向您介绍 5 种经过测试的方…

搜索之道:信息素养与终身学习的新引擎

&#x1f4d1;前言 在这个信息如同潮水般涌来的时代&#xff0c;我们每天都在与海量的数据和信息打交道。无论是学习、工作还是生活&#xff0c;我们都渴望能够迅速、准确地找到我们所需的信息。然而&#xff0c;面对如此繁杂的信息海洋&#xff0c;如何高效、精准地搜索到我们…

【python】tkinter GUI编程经典用法,Label标签组件应用实战详解

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Mac怎么读取内存卡 Mac如何格式化内存卡

在今天的数字化时代&#xff0c;内存卡已经成为了我们生活中不可或缺的一部分。对于Mac电脑用户而言&#xff0c;正确地读取和管理内存卡中的数据至关重要。下面我们来看看Mac怎么读取内存卡&#xff0c;Mac如何格式化内存卡的相关内容。 一、Mac怎么读取内存卡 苹果电脑在读…

用例与用例之间的三种关系:泛化、包含、扩展

UML用例图&#xff08;Use Case Diagrame)&#xff0c;是UML图的一种&#xff0c;主要用来描述角色及角色与用例之间的连接关系。 1.泛化 当多个用例共有一种类似的结构和行为时。能够将他们的共性抽象成为父用例&#xff0c;其它的用例作为泛化关系的子用例。箭头指向父用例…