学大伟业 Day 6 培训总结

今天接着昨天的继续讲数据结构

今天先是 分块

在统计问题中,尤其是序列问题,经常涉及到区间的操作,比如修改一段区间的元素,询问某个区间的元素的信息。

如果每次都对一整个区间的每一个元素进行操作的话,那可能就很笨重,所以怎么快速地统计某一段区间的信息就成为了问题所在。

我们考虑把整个序列分成若干个区间,每一个区间称为一个“块”,这样,修改或者询问的每一个区间都能被表示为若干个连续的整块和若干个单个元素的拼接。

对于连续的整块,我们直接询问或者修改这个整块的信息即可,对于单个的元素我们暴力遍历一遍。

如果我们设块的大小为S,块的个数为C的话,那么显然S*C≈N,并且一个区间会被分为不超过C个整块和不超过2S个单个元素,显然,取S=C=n^0.5最优。

分块的思想并不仅仅适用于序列,它实质上用到了一种均衡的思想。

分块算法是一种很常见的根号算法,一般它的时间复杂度会带根号。

分块和线段树的区别在于,分块算法可以维护一些线段树维护不了的东西,例如单调队列等,线段树能维护的东西必须能够进行信息合并,而分块则不需要。

不过,它们也有共同点,分块和线段树一样,分块需要支持类似标记合并的东西。

简单来说,分块算法就是优化过后的暴力。

至于代码...大概是这个样子...(嗯我不会告诉你我直接粘贴的mzx的资源)

 1 Block{
 2     int n,a[MAXN],belong[MAXN];
 3     int S,C,st[MAXN],ed[MAXN],sum[MAXN],delta[MAXN];
 4
 5     void pretreat(){
 6         S=int(sqrt(double(n)));
 7         for(int i=1;i<=n;i+=S){
 8             st[++C]=i;
 9             ed[C]=min(i+S-1,n);
10         }
11         for(int i=1;i<=C;i++)
12             for(int j=st[i];j<=ed[i];j++)
13                 belong[j]=i,sum[j]+=a[i];
14     }
15
16     int update(int x,int k){
17         a[x]+=k;
18         sum[belong[x]]+=k;
19     }
20
21     int query(int x,int y){
22         int l=belong[x],r=belong[y],ans=0;
23         if(l==r){
24             for(int i=x;i<=y;i++)
25                 ans+=a[i]+delta[belong[i]];
26         }
27         else{
28             for(int i=x;i<=ed[l];i++)
29                 ans+=a[i]+delta[belong[i]];
30             for(int i=l+1;i<r;i++)
31                 ans+=sum[i]+delta[i]*(ed[i]-st[i]+1);
32             for(int i=st[r];i<=y;i++)
33                 ans+=a[i]+delta[belong[i]];
34         }
35         return ans;
36     }
37
38 }

至于线段树...本博客里面已经写过一篇了,这里不再细说。

链接:http://www.cnblogs.com/MisakaAzusa/p/8485726.html

那么摘抄一下mxz ppt里面对线段树的解释

线段树是一种较为高级的数据结构,它用来维护区间的信息,我把它理解为更高级的一种分块方式:

根节点代表一整个区间[1,n],然后把它从中间等分为两个区间,作为它的两个儿子,然后再将这两个区间等分……如此递归下去,我们就构造出了一棵线段树。

线段树有多少个结点呢?

第1层有1个,第2层有2个,第3层有4个……第i层有2^(i-1)个。 但是最多只有O(logn)层,所以实际上节点数大约是2n个。

单点修改:

以维护最大值为例,我们在每个结点存储这个结点所对应的区间中所有元素的最大值,怎么初始化呢?

很简单,每个叶子结点都是一个长度为1的区间,对着序列直接DFS建树,叶子节点的值能直接确定,而一个非叶子结点的区间最大值就是它的两个儿子的最大值取更大那个,因为每个结点所对

应的区间都刚好是它另个儿子所对应的区间的合并。

于是,如果我们要修改某一个点的值,那么直接dfs到它所对应的那个叶子结点,在回溯的过程中更新包含这个点的区间即可,这些结点就是从根到这个叶子结点的一条链。

区间查询:

如果我们要询问一个区间,该怎么办?

回想一下分块的处理方法,一个区间能被划分为若干个整块和若干个点。

但是在线段树中,“点”也是一个块。 所以在询问区间时,我们直接对线段树进行DFS,如果一个结点所对应的区间被询问的区间完全覆盖了,那就把它的信息统计上并且不再往下搜索。

与线段树类似的,还有树状数组

树状数组(BIT)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下

进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。

树状数组和线段树很像,但能用树状数组解决的问题,基本上都能用线段树解决,而线段树能解决的树状数组不一定能解决。相比较而言,树状数组效率要高很多。

这里拿luogu P3374 树状数组模板为例

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int n,m;
 6 int tree[500070],a[500070];
 7 int lowbit(int x)
 8 {
 9     return x&-x;
10 }
11 void add(int k,int num)//给k位置的数值加num
12 {
13     while(k<=n)
14     {
15         tree[k]+=num;
16         k+=lowbit(k);
17     }
18 }
19 int sum(int k)//前缀和
20 {
21     int s=0;
22     while(k)
23     {
24         s+=tree[k];
25         k-=lowbit(k);
26     }
27     return s;
28 }
29 int main()
30 {
31     scanf("%d%d",&n,&m);
32     for(int i=1;i<=n;i++)
33     {
34         scanf("%d",&a[i]);
35         add(i,a[i]);
36     }
37     for(int i=1;i<=m;i++)
38     {
39         int c;scanf("%d",&c);
40         if(c == 1)
41         {
42             int x,k;
43             scanf("%d%d",&x,&k);
44             add(x,k);
45         }
46         else
47         {
48             int x,y;scanf("%d%d",&x,&y);
49             printf("%d\n",sum(y)-sum(x-1));
50         }
51     }
52     return 0;
53 }

原文地址:https://www.cnblogs.com/MisakaAzusa/p/8482154.html

时间: 2024-10-06 08:22:41

学大伟业 Day 6 培训总结的相关文章

学大伟业 Day 2 培训总结

一.dp 动态规划的本质 是一种思想.通过对原问题划分成子问题,寻找子问题之间的联系,通过求解子问题得出原问题的解.与贪心不同的是,动归是深谋远虑,考虑全局最优解:而贪心则目光短浅,只考虑局部最优解. 子问题 对应 状态 子问题之间的联系 对应 状态转移 边界子问题 对应 边界状态(状态转移的边界) 边界子问题:其结果不依赖其他子问题 求解动态规划类题目的要点 定义状态 寻找状态转移方程 边界状态 值得一提的是 动态规划中边界状态是可以由状态定义轻松得出 边界不能忽略. 动态规划的分类 按照常用

学大伟业 Day 3 培训总结

今天讲的字符串: 不多说,直接看题 一.表达式求值 题目大意: 输入一行一个表达式,计算其答案 表达式包含非负整数.加减乘除.括号 两种做法 ·栈 ·表达式树 这里更推荐表达式树,因为栈是先压进去,逆序操作.在进行逆序操作时即从右往左计算,实际应该是从左往右计算,所以会出现计算不符合顺序的问题.从而出现错误. 而表达式树则又称为"表达式目录树",以数据形式表示语言级代码,它是一种抽象语法树或者说是一种数据结构.--摘自百度百科 可见,每个父节点都是一种运算符,子节点为数字.运算时从底层

学大数据选自学还是培训呢

在你决定学一门技术时,那么有一个问题是你必须关注的,那就是有哪些学习方式,哪种学习方式好,哪种学习方式是你心仪的?当不同的学习方式产生冲突时,你该选择什么样的学习方式?比如当下学大数据有最常见的就是自学大数据和大数据培训,学大数据选自学还是培训呢? 自学大数据肯定是大部分朋友比较心仪的,谁不心疼钱呢?但是大数据培训效果比较好啊,这也是不可否认的,当这两种学习方式发生冲突的时,你该选择哪种学习方式呢?你心里有底吗?对于大数据的学习,长沙尚学堂小编的建议是选择大数据培训,理由很多. 大数据要学的东西

大数据开发经验分享:女生学大数据开发优势

在大数据时代下,大数据开发工程师和数据分析师都是非常多的人想要进入的行业.那么,作为女生学大数据难度大吗?女生学习大数据开发有什么优势吗?今天,我们针对此问题做个解答.一.与男性相比,女性在沟通上更有优势众所周知,做大数据就是为了服务于客户,虽然说做出项目可以满足于客户需求,但是如何完整地表达出你所做的项目初衷,则是不少男性大数据工程师的硬伤.女性给人平易近人的感觉,在与客户的沟通中会给客户以好感,自然能够减少与客户的沟通障碍.二.外界对女性工程师的期望值不高现在我们总是在强调男女平等,但是由于

零基础学大数据编程需要哪些基础?

零基础学大数据编程需要哪些基础?程序员薪酬高.工作环境好,是很多同学向往的职业,让很多非计算机专业的同学羡慕不已.非计算机专业难道就不能成为程序员了吗? 一.零基础学大数据编程需要基础: 1.数学基础 从计算机发展和应用的历史来看计算机的数学模型和体系结构等都是有数学家提出的,最早的计算机也是为数值计算而设计的.因此,要学好计算机就要有一定的数学基础,出学者有高中水平就差不多了. 2.逻辑思维能力的培养 学程序设计要有一定的逻辑思维能力,逻思力的培养要长时间的实践锻炼. 要想成为一名优秀的程序员

跟风舞烟学大数据可视化-Echarts从入门到上手实战

跟风舞烟学大数据可视化-Echarts从入门到上手实战 课程观看地址:http://www.xuetuwuyou.com/course/180 课程出自学途无忧网:http://www.xuetuwuyou.com 课程讲师:风舞烟 课时数:三个模块,共70课时   一.课程特色: 1.最全的Echarts课程讲解     70学时课时量,360度全方位,无死角的课程设计,让你通透Echarts可视化技术 2.最适合小白学员学习的课程,没有之一     只要你了解一点基本的Html,CSS,Ja

从0开始学大数据-Java运算符(3)

我们从零开始学习大数据技术,从java基础,到Linux技术涉猎,再深入到大数据技术的Hadoop.Spark.Storm技术,最后到大数据企业平台的搭建,层层递进,由点到面!希望技术大牛能过来指导学习. 上一节了解Java基础语法,本节我们开始学习Java基础-运算符,将会围绕以下几个知识点进行展开学习: 算术运算符 赋值运算符 比较运算符 逻辑运算符 位运算符 三元运算符 PS:开始之前我们先针对上节数据类型中补充的几个小问题 一.数据类型中补充的几个小问题 1.byte值的问题 byte

从0开始学大数据-Java基础-三元运算符/键盘录入(4)

我们从零开始学习大数据技术,从java基础,到Linux技术涉猎,再深入到大数据技术的Hadoop.Spark.Storm技术,最后到大数据企业平台的搭建,层层递进,由点到面!希望技术大牛能过来指导学习. 上一节了解Java运算符,其中三元运算符没有做讲解,本节我们开始学习Java基础-三元运算符/键盘录入,将会围绕以下几个知识点进行展开学习: 三元运算符 键盘录入数据 一.运算符 1.三元运算符 接着上一节的话题运算符,本节讲三元运算符,在讲三元运算符之前,可能会有很多朋友会问,是不是有一元运

从零开始学大数据-Java基础-switch语句(6)

我们从零开始学习大数据技术,从java基础,到Linux技术涉猎,再深入到大数据技术的Hadoop.Spark.Storm技术,最后到大数据企业平台的搭建,层层递进,由点到面!希望技术大牛能过来指导学习. 上一节学习了流程控制语句,本节学习switch语句. 开始之前,我们先看一下上节的练习题. 一.练习题 if语句格式的练习: 1.获取两个数据中较大的值 2.判断一个数据是奇数还是偶数,并输出 程序执行结果如下: 二.switch语句 流程控制语句的选择结构 1 选择结构(也被称为分支结构)