主席树入门学习 洛谷p3834 和 牛客 换个角度思考(题的名字)

本文详细介绍了主席树和线段树的概念、应用及代码实现。通过解决洛谷P3834和牛客19427两道题,深入探讨了主席树的建树、查询操作,以及离散化处理。附带完整AC代码,适合初学者学习。

两道题的链接
洛谷 https://www.luogu.com.cn/problem/P3834
牛客 https://ac.nowcoder.com/acm/problem/19427

本来是写专题写到自闭想去牛客写点简单的题目放松一下
结果找到了这道题
拿线段树写了下 t掉了
看题解说是主席树裸题
然后就开始了一整天的主席树学习
又是自闭的一天
到现在其实对主席树的板子的理解还是有一点模模糊糊的
推荐一个主席树视频吧
https://www.bilibili.com/video/BV1C4411u7rK?p=2
让我从完全看不懂代码到现在基本上能够理解了

主席树真是一个让人自闭的数据结构
它是由多个权值线段树优化而来
用来记录一个权值线段树的所有历史版本

权值线段树的博客推荐
https://www.cnblogs.com/fusiwei/p/12234435.html

以下是主席树的板子分解

首先是应为主席树是权值线段树变来的
所以叶子节点记录的都是某个数值出现的次数
但题目数据过大时我们就需要使用离散化来处理线段树的数据

vector<int>v;
int getid(int x)
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}

	for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    //之后直接用getid函数即可获得离散化后的数据

然后是关于主席树的操作
首先是建树

void Insert(int l,int r,int pre,int &now,int p)//这里的now表示的是新建的树 直接在数组里面赋值 所以要有&
{
    hjt[++cnt]=hjt[pre];//将之前的树的数据拷贝给新的
    now=cnt;
    hjt[now].sum++;//含有新树插入值的区间需要加一
    //之后就是线段树的正常建树方法
    if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)
        Insert(l,mid,hjt[pre].l,hjt[now].l,p);
    else
        Insert(mid+1,r,hjt[pre].r,hjt[now].r,p);
}

然后是查询

int query(int l,int r,int L,int R,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    //令我们要求的区间的右端点为r 左端点为l 将r的树减去l-1的树 剩下的就是从l到r的树了 可以类比前缀和的思想进行理解 这里用的是l是因为在查询时做了处理
    int tem=hjt[hjt[R].l].sum-hjt[hjt[L].l].sum;
    if(k<=tem)return query(l,mid,hjt[L].l,hjt[R].l,k);//与权值线段树的查询方式相同
    else return query(mid+1,r,hjt[L].r,hjt[R].r,k-tem);
}

主席树初步的代码就这么多 主要还是难在理解它建树时与之前树连接起来的操作

洛谷p3834 ac代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
using namespace std;
const int maxn = 2e5+10;
const int inf = 0x1f1f1f1f;
const int mod = 2333;

int a[maxn];
int n,m;
struct node
{
    int l,r,sum;
}hjt[maxn<<5];
int cnt,root[maxn];
vector<int>v;

int getid(int x)
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}

void Insert(int l,int r,int pre,int &now,int p)
{
    hjt[++cnt]=hjt[pre];
    now=cnt;
    hjt[now].sum++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)
        Insert(l,mid,hjt[pre].l,hjt[now].l,p);
    else
        Insert(mid+1,r,hjt[pre].r,hjt[now].r,p);
}

int query(int l,int r,int L,int R,int k)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int tem=hjt[hjt[R].l].sum-hjt[hjt[L].l].sum;
    if(k<=tem)return query(l,mid,hjt[L].l,hjt[R].l,k);
    else return query(mid+1,r,hjt[L].r,hjt[R].r,k-tem);
}

int main()
{
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=1;i<=n;i++)
    {
        Insert(1,n,root[i-1],root[i],getid(a[i]));
    }
    while(m--)
    {
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        int tem=query(1,n,root[l-1],root[r],k);
        printf("%d\n",v[tem-1]);//a数组是从1开始 而vector则是从0开始 所以要减1
    }
}

牛客 换个角度思考

这个题因为问的是a[i]<=k的个数
所以不离散化
直接查询
查询的函数也有些变化

ac代码

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
using namespace std;
const int maxn = 2e5+10;
const int inf = 0x1f1f1f1f;
const int mod = 2333;

int a[maxn];
int n,m;
struct node
{
    int l,r,sum;
}hjt[maxn<<5];
int cnt,root[maxn];
vector<int>v;

int getid(int x)
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}

void Insert(int l,int r,int pre,int &now,int p)
{
    hjt[++cnt]=hjt[pre];
    now=cnt;
    hjt[now].sum++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)
        Insert(l,mid,hjt[pre].l,hjt[now].l,p);
    else
        Insert(mid+1,r,hjt[pre].r,hjt[now].r,p);
}

int query(int l,int r,int pre,int now,int L,int R)
{
    if(r<=R) return hjt[now].sum-hjt[pre].sum;
    int mid=(l+r)>>1;
    if(R<=mid)return query(l,mid,hjt[pre].l,hjt[now].l,L,R);
    else if(L>mid)return query(mid+1,r,hjt[pre].r,hjt[now].r,L,R);
    else return query(l,mid,hjt[pre].l,hjt[now].l,L,mid)+query(mid+1,r,hjt[pre].r,hjt[now].r,mid+1,R);
}

int main()
{
    //freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
    scanf("%d%d",&n,&m);
    int maxx=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        maxx=max(maxx,a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=1;i<=n;i++)
    {
        Insert(1,maxx,root[i-1],root[i],getid(a[i]));
    }
    while(m--)
    {
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        int tem=query(1,maxx,root[l-1],root[r],1,k);
        printf("%d\n",tem);
    }
}

代码下载地址: https://pan.quark.cn/s/a4b39357ea24 在计算机视觉技术中,数据集扮演着训练评估模型的核心角色。Labelme作为一个广受欢迎的开源工具,能够支持用户以交互方式对图像进行标注,而COCO(Common Objects in Context)则是一种被广泛采纳的数据集标准格式,适用于包括物体检测、图像分割在内的多种任务。本文将详细阐述如何将Labelme生成的标注数据转为COCO数据集的标准格式。 Labelme标注的图像在输出为JSON格式时,会包含以下核心内容: 1. `version`: 指明JSON文件的版本信息。 2. `flags`: 目前未定义或保持为空,预留用于未来的功能扩展。 3. `shapes`: 列表形式存储对象的形状信息,每个形状项包含`label`(对象类别名称),`points`(构成对象边缘的多边形顶点),以及`shape_type`(通常为“polygon”)。 4. `imagePath``imageData`: 提供原始图像的存储路径二进制数据,便于后续图像的还原。 5. `imageHeight``imageWidth`: 明确标注图像的垂直水平尺寸。 COCO数据集的标准格式中定义了三种主要的标注类型: 1. Object instances(目标实例):主要用于执行物体检测任务。 2. Object keypoints(目标上的关键点):适用于人体姿态估计相关应用。 3. Image captions(看图说话):用于生成图像的文本描述。 COCO的JSON结构中包含以下基本组成部分: 1. `images`:记录图像的基本属性,包括`height`(高度)、`...
内容概要:本文围绕基于Basisformer模型的时间序列锂离子电池SOC(State of Charge,荷电状态)预测展开研究,利用PyTorch深度学习框架构建并训练模型,旨在提升锂电池SOC估计的准确性与鲁棒性。该方法融合Transformer架构的核心机制,通过引入基函数(Basis)分解策略,有效捕捉电池充放电过程中长时序、非线性动态特征,增强模型对复杂工况的适应能力。研究不仅详细阐述了Basisformer的网络结构设计、注意力机制优化与训练流程,还提供了完整的Python代码实现方案,涵盖数据预处理、模型搭建、损失函数定义、训练验证及结果可视化等环节,便于科研人员快速复现、调优并拓展至其他电池状态预测任务。; 适合人群:具备一定深度学习与Python编程基础,熟悉PyTorch框架,从事电池管理系统(BMS)、新能源汽车、储能系统、智能传感等领域的高校研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于动力电池与储能系统的实时SOC估算模块,提升系统安全性与能量利用效率;②作为学术研究的基础模型,用于复现、改进基于Transformer的时间序列预测方法在电化学系统中的应用;③为数据驱动的电池健康状态(SOH)、剩余使用寿命(RUL)联合估计提供可扩展的技术框架。; 阅读建议:建议读者结合所提供的代码与公开电池数据集(如NASA、CALCE等)进行动手实践,深入理解模型的输入输出结构与时序建模逻辑,同时可尝试引入温度、老化周期等多维特征,或融合物理模型构建混合预测架构,以进一步提升预测精度与泛化能力。
内容概要:本文系统阐述了基于动态规划算法优化插电式混合动力电动汽车(PHEV)能源管理的技术方案,结合Matlab与Simulink工具实现完整的仿真建模与代码开发。通过动态规划这一全局优化方法,在已知驾驶循环条件下,精确求解发动机、电机及电池之间的最优能量分配策略,以实现燃油消耗与排放的最小化目标,解决PHEV多能源路径规划中的复杂决策问。文中提供了详尽的仿真模型构建流程与算法实现步骤,涵盖车辆动力学建模、能量管理架构设计、状态空间定义、代价函数构造、最优控制律求解及结果可视化分析等关键环节,全面揭示PHEV能量管理系统的内在机制与优化逻辑。; 适合人群:具备一定Matlab/Simulink编程基础,从事新能源汽车、智能控制、电力电子、自动化或交通运输工程等相关领域的研究生、科研人员及工程技术人员,尤其适合专注于车辆能量管理策略、节能控制算法研究的专业人士。; 使用场景及目标:①深入掌握动态规划在混合动力汽车能量管理中的理论基础与工程实现方法;②学习如何在Matlab/Simulink环境中搭建PHEV整车仿真平台并实施多目标优化仿真;③为学术研究、学位论文撰写或实际工程项目提供可复用的算法框架、模型模板与技术支持,支撑后续对等效燃油消耗最小化策略(ECMS)、模型预测控制(MPC)、实时优化算法等的对比研究与性能评估。; 阅读建议:建议读者结合所提供的完整代码与Simulink模型文件,逐模块调试运行,重点理解状态变量离散化处理、前后向递推求解过程、惩罚项设置以及边界条件处理等核心技术细节,同时可进一步拓展应用于不同工况场景、不同车型结构或与其他优化算法(如庞特里亚金极小值原理PMP)的对比验证,从而深化对PHEV能量管理实时性与全局性平衡问的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值