「Day 7—离散化 树状数组 线段树」

news/2024/9/17 21:43:35/文章来源:https://www.cnblogs.com/To-Carpe-Diem/p/18354001

离散化

定义

离散化本质是一种哈希,是一种用来处理数据的方法。
1.创建原数组的副本。
2.将副本中的值从小到大排序。
3.将排序好的副本去重。
4.查找原数组的每一个元素在副本中的位置,位置即为排名,将其作为离散化后的值。

B3694 数列离散化

代码

#include<iostream>
#include<algorithm>
using namespace std;const int MAXN = 1e5 + 5;
int n;
int T; 
int a[MAXN],tmp[MAXN];void lsh(){for(int i = 1;i <= n;i ++){//复制一个数组用来去重用tmp[i] = a[i];}sort(tmp + 1,tmp + n + 1);//去重int len = unique(tmp + 1,tmp + n + 1) - tmp - 1;for(int i = 1;i <= n;i ++){//离散a[i] = lower_bound(tmp + 1,tmp + len + 1,a[i]) - tmp;}for(int i = 1;i <= n;i ++){cout << a[i] << " ";}cout << "\n";
}int main(){cin >> T;while(T --){cin >> n;for(int i = 1;i <= n;i ++){cin >> a[i];}lsh();}return 0;
}

树状数组

定义

树状数组是一种支持 单点修改区间查询 的数据结构。
下图是一个树状数组的示意图,

具体的看代码

P3374 【模板】树状数组 1

代码

#include<iostream>
using namespace std;const int MAXN = 5 * 1e5 + 5;
int n,m;
int sum[MAXN];int lowbit(int x) {// x 的二进制中,最低位的 1 以及后面所有 0 组成的数。// lowbit(0b01011000) == 0b00001000//          ~~~~^~~~// lowbit(0b01110010) == 0b00000010//          ~~~~~~^~return x & -x;
}
//将x加上k
void add(int x,int k){while(x <= n) sum[x] += k,x += lowbit(x);
}//计算从1 ~ x的和
int f(int x){int ans = 0;while(x) ans += sum[x],x -= lowbit(x);return ans;
}int main(){cin >> n >> m;for(int i=1;i<=n;i++){int x;cin >> x;add(i,x);}for(int i = 1;i <= m;i ++){int op,x,y;cin >> op >> x >> y;if(op == 1) add(x,y);else cout << f(y) - f(x - 1) << "\n";}
}

P3368 【模板】树状数组 2

代码

#include<iostream>
using namespace std;const int MAXN = 5 * 1e5 + 5;
int n,m;
int sum[MAXN],a[MAXN];int lowbit(int x){return x&-x;
}
void add(int x,int k){while(x <= n) sum[x] += k,x += lowbit(x);
}
int f(int x){int ans = 0;while(x) ans += sum[x],x -= lowbit(x);return ans;
}int main(){cin >> n >> m;for(int i=1;i<=n;i++){cin >> a[i];}for(int i = 1;i <= m;i ++){int op,x,y,k;cin >> op >> x;if(op == 1){cin >> y >> k;//运用差分的思想 差分数组在x处+k,在y+1处-k就完成了对x~y的区间加k问题add(x,k);add(y + 1,- k);}else cout << a[x] + f(x) << "\n";}
}

线段树

定义

线段树是为了维护区间信息的一种数据结构,线段树可以在 \(O(\log N)\) 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。

P3372 【模板】线段树 1

思路

我们用递归建树来完成,具体的看代码。

代码

定义结构体
struct node{
//l是左子树的节点编号,r是右子树的节点编号,sum为当前节点的区间和(l~r),add是当前节点的懒惰标记。int l,r,sum,add;
}t[MAXN * 4]; 
上传操作
void pushup(int p){
//当前节点的区间和=左子树的区间和+右子树的区间和t[p].sum = t[lc].sum + t[rc].sum;return;
}
下传操作
void pushdown(int p){
//判断当前节点是否有懒惰标记,如果有就下放给其左右子树if(t[p].add){//下放左子树t[lc].add += t[p].add;//下放右子树t[rc].add += t[p].add;//同时也要将懒惰标记的数值下放给左右子树的区间值//左子树的区间+区间元素数量*懒惰标记t[lc].sum += t[p].add * (t[lc].r - t[lc].l + 1);//右子树的区间+区间元素数量*懒惰标记t[rc].sum += t[p].add * (t[rc].r - t[rc].l + 1);//下放完成后将根节点的懒惰标记清0t[p].add = 0;}
}
建树
void build(int p,int l,int r){//我们采用递归建树的方式t[p] = {l,r,0,0};//l = r说明这是一个叶子结点,则区间值也就是(l~r)的值为其本身的值x[l]或x[r],这个无所谓if(l == r){t[p].sum = x[l];return;}//位运算避免溢出,等同于(l + r) / 2 或者 (l + r) >> 1int mid = (l & r) + ((l ^ r) >> 1);//以lc(p << 1)为左子树的根建立以l为区间左端点,mid为区间右端点的左子树build(lc,l,mid);//以rc (p << 1 | 1) 为右子树的根建立以mid+1为区间左端点,r为区间右端点的右子树build(rc,mid + 1,r);//进行上传操作,上传的原因是因为既然左子树和右子树都建立了,且其区间值也计算完毕,那么就应该利用左右子树向上传递其区间值。//例如p为【1~2】的区间,左子树lc【1~1】的值为4,右子树rc【2~2】的值为6.//那么p的值此时为lc.sum + rc.sum,即4 + 6 = 10,。pushup(p);
}
区间查询
int query(int p,int l,int r){//如果当前节点 p 代表的区间 [t[p].l, t[p].r] 完全在我们查询的区间 [l, r] 之内//(即 l <= t[p].l 且 t[p].r <= r),那么我们可以直接返回这个节点的和 t[p].sum,而不需要进一步递归地查询子节点。if(l <= t[p].l && t[p].r <= r) return t[p].sum;//依旧是位运算,求的是当前节点p的区间中点int mid = (t[p].l & t[p].r) + ((t[p].l ^ t[p].r) >> 1);//下传一下懒惰标记,防止没有下传导致查询出错pushdown(p);int sum = 0;//如果要查的区间的左端点比mid小,说明还要向左子树继续递归求值if(l <= mid){sum += query(lc,l,r);}//如果要查的区间的右端点比mid大,说明还要向右子树继续递归求值if(r > mid){sum += query(rc,l,r);}//最后返回左右子树的值之和return sum;
}
区间修改
void change(int p,int l,int r,int k){//如果区间刚好对上了,那么就让当前的节点的区间值+区间元素*kif(l <= t[p].l && t[p].r <= r){t[p].sum += (t[p].r - t[p].l + 1) * k;//懒惰标记,方便下传t[p].add += k;return;}//如果没对上说明还是有左右子树需要继续修改//又双叒是位运算,求的是当前节点p的区间中点int mid = (t[p].l & t[p].r) + ((t[p].l ^ t[p].r) >> 1);//下传懒惰标记,防止在修改子树是区间值不对pushdown(p);//如果要修改的区间的左端点比mid小,说明还要向左子树继续递归修改if(l <= mid) change(lc,l,r,k);//如果要修改的区间的右端点比mid大,说明还要向右子树继续递归修改if(r > mid) change(rc,l,r,k);//修改完别忘了去给p更新值pushup(p);
}

完整代码

#include<iostream>
#define lc p << 1
#define rc p << 1 | 1
#define int long long
using namespace std;const int MAXN = 1e5 + 5;
int n,m,x[MAXN];
struct node{int l,r,sum,add;
}t[MAXN * 4]; 
//上传 
void pushup(int p){t[p].sum = t[lc].sum + t[rc].sum;return;
}
//下传 
void pushdown(int p){if(t[p].add){t[lc].add += t[p].add;t[rc].add += t[p].add;t[lc].sum += t[p].add * (t[lc].r - t[lc].l + 1);t[rc].sum += t[p].add * (t[rc].r - t[rc].l + 1);t[p].add = 0;}
}
//建树 
void build(int p,int l,int r){
// 	t[p] = {l,r,x[l],0};t[p] = {l,r,0,0};if(l == r){t[p].sum = x[r];return;}int mid = (l & r) + ((l ^ r) >> 1);build(lc,l,mid);build(rc,mid + 1,r);pushup(p);
}
int query(int p,int l,int r){if(l <= t[p].l && t[p].r <= r) return t[p].sum;int mid = (t[p].l & t[p].r) + ((t[p].l ^ t[p].r) >> 1);
// 	int mid = (t[p].l + t[p].r) / 2;pushdown(p);int sum = 0;if(l <= mid){sum += query(lc,l,r);}if(r > mid){sum += query(rc,l,r);}return sum;
}
void change(int p,int l,int r,int k){if(l <= t[p].l && t[p].r <= r){t[p].sum += (t[p].r - t[p].l + 1) * k;t[p].add += k;return;}int mid = (t[p].l & t[p].r) + ((t[p].l ^ t[p].r) >> 1);pushdown(p);if(l <= mid) change(lc,l,r,k);if(r > mid) change(rc,l,r,k);pushup(p);
}signed main(){cin >> n >> m;for(int i = 1;i <= n;i ++){cin >> x[i];}build(1,1,n);for(int i = 1;i <= m;i ++){int op,x,y,k;cin >> op >> x >> y;if(op == 1){cin >> k;change(1,x,y,k); }else{cout << query(1,x,y) << "\n";}} return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chrome无法下载文件

用的电脑是Mac Air M1,安装了127版本的chrome,总有几次在浏览器上下载文件的时候明明点击了下载按钮,chrome图标显示已经有下载的内容了,但是打开下载文件夹却不显示显示下载的文件,扒拉看了一下说是迅雷插件要关闭,后面发现自己也没安装迅雷插件。解决方案: 把这两个关…

万兆以太网协议栈的FPGA实现(一):常识+PCSPMA+结构

参考: https://docs.amd.com/r/zh-CN/pg210-25g-ethernet/%E7%AE%80%E4%BB%8B?tocId=59kIPN67Q57xorWh9w6GTA 10GbE以太网MAC和PHY - 者旨於陽 - 博客园 (cnblogs.com) 什么是CML电平-CSDN博客 10G Ethernet PCS/PMA v6.0 Product Guide (PG068) • 查看器 • AMD 技术信息门…

Two-Processor Scheduling 学习笔记

再高没有高爸高。为什么有人联考放论文题啊?不过好有趣。参考的 glx 博客。 考虑这么一个问题,给定一张偏序图,即一个满足传递性和非自反性的偏序关系 \(\succ\) 连成的 DAG。你需要对这张图进行拓扑排序,每次可以同时删去一个或者两个零入度点,问最少删多少次可以把图删空…

【原创软件】第8期:pdf转图片软件FAST_PDF2PNG,可选分辨率快速pdf转图片

一、背景 因为经常处理扫描件,需要将扫描件或者电子书先转为图片进行优化处理。为了省去人工时间,制作了一个软件FAST_PDF2PNG。尽管已经有不少软件可以完成该功能,但是杀鸡焉用牛刀。 本工具仅约4M,小巧方便。(优点:速度快,比pdf补丁丁更快,可选分辨率72-1200dpi,含有…

2024暑假集训测试23

前言比赛链接。T2 部分分给得特别足,\(60pts\),而且他不可能剩下的数据全放菊花,所以得到了 \(76pts\),但赛时想了很长时间正解,没有想出来,给后面题剩的时间不多,就都胡暴力了,\(T4\) 甚至忘了剪枝,剪完之后 \(20pts\to 60pts\) 没绷住。 说到这儿要吐槽一下 T4 数据…

了解LSTM网络(英文博客汉化)

Understanding LSTM Networks-了解LSTM网络原文来自于大神Cristopher Colah于2015年在Github上发布的一篇博客, 窃以为此文不失为一篇入门神经网络的经典文章, 遂产生了汉化的想法, 附原文链接Understanding LSTM Networks什么是RNN 人类不会时时刻刻都从头开始思考。譬如当你读…

[Spring]Bean生命周期

生命周期的概要流程 Bean 的生命周期概括起来就是 4 个阶段:实例化(Instantiation) 属性赋值(Populate) 初始化(Initialization) 销毁(Destruction)实例化:第 1 步,实例化一个 bean 对象; 属性赋值:第 2 步,为 bean 设置相关属性和依赖; 初始化:第 3~7 步,步骤…

4-反向传播

从这张图片,可以看出。不管我们有多少层,都可以化简成最简形式。我们添加的层就没有意义。 所以我们要对每一层的输出做非线性变换,增加模型的复杂程度,使它没法化简反向传播的流程pytorch的tensor数据结构看到代码,要能把计算图构建出来,而不是简单的乘除运算在上面这张…

QSortFilterProxyModel和QTreeView排序功能

1、需求,创建一个树有多层结构,同一层按照插入顺序逆序排列; ui.treeView->setHeaderHidden(true); //tree widget头标题是否显示,此处隐藏标题//ui.treeWidget->header()->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);ui.treeView->heade…

【数值计算方法】非线性方程求根-数值实验

数值实验python实验本文来自博客园,作者:FE-有限元鹰,转载请注明原文链接:https://www.cnblogs.com/aksoam/p/18355743

图计数(三个思想,贼重要,紫题,非常有东西)

https://www.luogu.com.cn/problem/AT_abc180_f 第3题 图计数 查看测评数据信息给n个节点m条边,构造一些无向图,构造出来的图需要满足以下条件: (1)图中没有自环 (2)图中每个点的度最大是2 (3)图中连通块大小最大为L 问能构造出多少个这样的图出来,答案可能很大,…

CSP19

没啥可说的,暴力大赛水题,贪心的尽量向右构造即可点击查看代码 #include <bits/stdc++.h> #define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); #define ll long long #define pb push_back #define ull unsigned long long #define pii pair<int…