23 |
24 | 在看这篇文章的时,我相信你已经掌握了“快速幂”算法。众所周知,快速幂是一种利用倍增思想将求幂运算的复杂度从O(n)降低到O(logn)的经典算法,其大致代码如下所示:
25 |
26 |
27 |
28 |
29 | 对于一般的求幂,这个算法已经非常优了。然而如果碰上了要求的指数非常大,以致于超过long long,达到了高精度,比如有上万位的时候,快速幂也显得无能为力了。
30 |
31 |
32 | 不过通常来说,求快速幂都会进行取模操作。如果模一个素数p的话,我们可以把复杂度进一步降低。
33 |
34 |
35 | 为此,我们首先需要引入欧拉定理和拓展欧拉定理:
36 |
37 |
38 |

39 |
40 |
41 |

42 |
43 |
44 | 这两个定理的证明需要用到数论相关的理论,且证明步骤较为复杂,在这里略去。有兴趣的可以参考oi wiki。
45 |
46 |
47 | 上式的φ(m)是欧拉函数,返回一个整数,代表在整数区间[1,m]中与m互质的整数数目,求一个数的欧拉函数可以用如下的代码:
48 |
49 |
50 |
51 | 接下来,我们只需要直接套用快速幂和欧拉函数代码进行组合,即可用拓展欧拉定理求幂,由于指数变成了(b mod φ(m))+φ(m),所以代码时间复杂度是O(log((b mod φ(m))+φ(m))),b较大时,比O(log(b))大为优化。整体代码如下:
52 |
53 |
54 |
55 | 希望这篇帖子能够对读者有所帮助~
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/post1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
24 | 如果你学习的第一门语言就是C语言,那么我想你一定因为输入数据的问题而困扰过吧……C语言的输入方式是如此的庞多且繁杂,有诸如printf, getchar, gets, fgets, getch, fread, freopen等大量与输入有关的函数,而又因为许多特殊字符,从一般的空格、换行符到庞多的占位符、其他转义符的存在,足以让初学者眼花缭乱,从而在Hello world面前便望而却步……所以现在我便分享一下我个人对C语言输入相关的浅薄的见解。
25 |
26 |
27 | 注:因篇幅有限,本篇内容只针对较为难理解的输入流部分的内容,而对占位符、转义符等相对简单的内容不做讨论,并且只讨论从标准输入流(stdin),即标准输入设备,一般是键盘输入内容,而不讨论文件读写。下面仅以printf,getchar和gets三种最常用的输入函数为例。并且为了通俗起见,下文内容可能可能会有不严谨的地方,严谨的表述请以
28 |
C/C++标准 为准。
29 |
30 |
31 | 本帖大致会从以下两个方面对输入流读入机制进行简单的分析:
32 |
输入流简单介绍 、
33 |
激活和获取输入流 。
34 |
36 |
37 |
38 | 什么是输入流?
39 |
40 |
41 | 一个程序的 输入流 是使用程序的用户从程序启动开始在程序内输入的字符序列队列。 队列 是一种线性数据结构,可以大致理解成是一个长度灵活变化的数组,并且遵循里面的元素是先进先出的原则。这个数组的第一个元素叫做 队首,最后一个元素叫做 队尾 。先进先出指的是每次从输入流获取一个元素,能且仅能获取队首,并且获取完毕之后,队首元素会被移除出队,下一个元素成为新的队首,这个过程称为
42 | 出队;并且,每次新插入队的元素一定是放置在原本队尾的后面成为新的队尾的,这个过程称为 入队。此外,如果一个队列没有元素,称为
43 | 队空。
44 |
45 |
46 | 输入流是一种队列。运行程序时,在一些时机(下文会具体分析是什么时机)下,用户从键盘输入的内容会依据输入时间的先后,依次入队输入流,在这里我们称之为 激活输入流。而输入函数(如printf, getchar, gets)则会从输入流中获取队首元素并让队首出队,这个过程会执行若干次,不同的输入函数有不同的执行机制,这个过程在这里我们称之为 获取输入流。
47 |
48 |
49 | 下面举一个熟知的例子先粗略理解一下上述的文字,假设我们有一样的一段代码:
50 |
51 |
52 |
53 | 对上面的代码,假设我们输入了“hello world and everything!”,那么程序的运行如下图所示:
54 |
55 |
56 |

57 |
58 |
59 | 在上面的例子中,当我们输入回车(无论是按下回车还是粘贴的内容出现了回车的粘贴中还未完毕的那一刻)的时候,激活了输入流。激活之后,输入流可以粗略看成是一个长为28个的字符队列,队首是h,队尾是换行符\n(注意,队尾不是感叹号)。
60 |
61 |
62 | 注:准确来说,按下回车并不必然意味着输入的是\n,只不过在多数操作系统是如此。在一些其他操作系统下,按下回车实际上输入的可能是\r,或者是\r\n等。
63 |
64 |
65 | 这时候程序的scanf语句获取输入流,根据%s占位符规则(下文会详细介绍),连续出队了五个char字符元素,按照时间先后分别是'h','e','l','l','o',这五个字符存进了char数组变量x中;由于还有另外一个%s,所以随后出队' ',它没有存进任何变量中;接下来,再次连续出队五个char字符元素'w','o','r','l','d',存进了变量y中,此时,停止获取输入流。所以当scanf语句结束后,此刻的输入流队首是第二个空格,第二个元素是'a',输入流长度是27-11=16。
66 |
67 |
68 | 有了一个简单的例子作了铺垫,接下来可以详细分析一下激活输入流和获取输入流的详细机制了。
69 |
70 |
71 | 激活和获取输入流
72 |
73 |
74 | 本篇仅介绍
scanf及其常用参数(包括一般字符和占位符) 、
75 |
getchar和gets 的最基本的激活输入流规则。
76 |
77 |
78 | scanf的激活和获取输入流规则
79 |
80 |
81 | scanf的激活输入流的规则是:如果执行到scanf语句的时候,输入流不为队空状态,那么直接获取输入流。如果获取输入流尚未结束时触发了队空,那么激活输入流,继续获取,如此往复,直到获取输入流结束,则scanf语句执行完毕。用伪代码表示流程如下:
82 |
83 |
84 |
85 | 接下来介绍scanf占位符(主要包括%c,%d(%lld),%f(%lf),%s四种)和一般字符获取输入流规则。首先介绍单个占位符的scanf语句机制:
86 |
87 |
88 | %c(或%*c)的获取输入流机制为:直接取一次队首赋值给对应变量,接着队首出队,然后结束获取输入流。伪代码如图所示:
89 |
90 |
91 |
92 | %d(或%lld,%o,%u,%hd,%x,%2d等所有整数占位符)的获取输入流机制为:首先预处理,如果队首是空白字符(主要是空格和回车,即isspace函数返回值是1),清除空白字符,出队,如果不是空白字符,结束预处理;如果队首是数字(即isdigit函数返回值是1)或正负号,那么继续读取,并让队首出队;否则(如读到回车)队首不出队,且结束获取输入流,将已经读到的所有内容赋值给对应变量。
93 |
94 |
95 | 空白字符包括空格' ',回车'\n','\r',制表符'\t','\v'等。
96 |
97 |
98 | 正负号能且只能读取一个,读取到第二个直接结束获取输入流,如输入若干空格和回车的组合之后输入+-3只读取到+,然后结束获取输入流,输入流为"+-3",scanf结束后,输入流为"-3\n";而输入-500后读取到-500,然后因为回车字符结束获取输入流,scanf结束后,输入流为"\n"。
99 |
100 |
101 | 伪代码如下:
102 |
103 |
104 |
105 | %f(或%lf,%Lf等所有浮点数占位符)的获取输入流机制为:首先进行同上的预处理,然后如果读取到正负号和小数点规则也是一次原则,如果读取到指数符号一次,那么后面还可以跟一次正负号和数字(或什么也不跟,但不能再跟小数点),上述读取成功后,队首出队,继续读取;其他情况下队首不出队,赋值变量,结束获取输入流。
106 |
107 |
108 | 注意小数点可以省略前导零或后导零的其中一个,如3.代表3.0,.7代表0.7,此时输入流均为"\n";而单独一个.结束获取输入流,此时输入流为"\n"
109 |
110 |
111 | 伪代码如下:
112 |
113 |
114 |
115 | %s的获取输入流机制为:首先进行同上的预处理,然后如果读到第一个非空白字符,预处理结束,队首出队,继续处理,直到再次读到了一个空白字符,队首不出队,赋值变量,结束获取输入流。伪代码如下:
116 |
117 |
118 |
119 | 对于一般的单个字符,获取输入流机制如下:如果队首就是这个字符,直接出队,结束获取输入流;否则不出队,直接结束获取输入流。伪代码如下:
120 |
121 |
122 |
123 | 对于多个占位符和一般字符的组合参数,对参数从左往右执行上述的规则,执行过程中如果任何一个单一参数处结束了输入流,则直接结束该scanf语句,即使后面还有未被处理的参数也不予处理,伪代码如下:
124 |
125 |
126 |
127 | 下面举例对上述机制进行阐述,对于下列程序:
128 |
129 |
130 |
131 | 输入文本如下:
132 |
133 |
134 |
135 | 程序输出如下:
136 |
137 |
138 |
139 | 详细过程解析如下:
140 |
141 |
142 | 程序开始运行,并且运行到第8行的scanf,程序暂时中止,等待用户输入。
143 |
144 |
145 | 用户输入到第一行末尾的回车的时候(如果是亲手输入,那就是按下回车那一刻,如果是粘贴全部文本,那就是粘贴过程中刚好粘贴到第一个回车的时候马上暂时中止粘贴,然后激活)激活输入流,此时,输入流是第一行文本,即"+-5.3e-1.2\n",此时开始运行第八行代码。
146 |
147 |
148 | 执行第八行代码scanf("%d", &i1),读取了第一个正号,由于正负号只能出现一次,所以到负号时停止,没有对i1进行任何赋值,获取输入流结束,此时输入流为"-5.3e-1.2\n",第八行代码执行结束。
149 |
150 |
151 | 执行第九行代码scanf("%c", &c1),由于输入流不是队空,所以继续获取,将'-'赋值给c1,获取输入流结束,此时输入流为"5.3e-1.2\n",第八行代码执行结束。
152 |
153 |
154 | 执行第十行代码scanf("%lf", &f1),由于输入流不是队空,所以继续获取,将5.3e-1赋值给f1,由于指数部分出现了'.',所以获取输入流结束,f2=5.3e-1即5.3乘以10的-1次方,即f2=0.53,此时输入流为".2\n",第十行代码执行结束。
155 |
156 |
157 | 执行第十一行代码scanf("%lf", &f2),由于输入流不是队空,所以继续获取,将.2赋值给f2,由于之后读取到了换行符,所以获取输入流结束,f2=.2=0.2,此时输入流为"\n",第十一行代码执行结束。
158 |
159 |
160 | 执行第十二行代码scanf("%lf", &f3),发现空白字符,进行预处理,预处理后输入流队空,所以等待用户继续输入;用户输入第二行,是回车,还是空白字符,处理后队空,所以继续等待,第三行同理,然后读入了第四行,此时输入流为" 5.gc lr580 53c d56 e\n",预处理掉前三个空格,然后读取走"5.",遇到了g,获取输入流结束,f3=5.=5.0,此时输入流为"gc lr580 53c d56 e\n",第十二行代码执行结束。
161 |
162 |
163 | 执行第十三行代码scanf("g"),要读取到的恰好是g,直接出队(如果不是那么不对出队,且获取输入流结束),获取输入流结束,此时输入流为"c lr580 53c d56 e\n",第十三行代码执行结束。
164 |
165 |
166 | 执行第十四行代码scanf("%c", &c2),将'c'赋值给c2,获取输入流结束,此时输入流为" lr580 53c d56 e\n",第十四行代码执行结束。
167 |
168 |
169 | 执行第十五行代码scanf("%s", s),预处理掉一个空格,然后读取到"lr580"赋值给s,然后遇到空白字符' ',获取输入流结束,此时输入流为" 53c d56 e\n",第十五行代码执行结束。
170 |
171 |
172 | 执行第十六行代码scanf("%d%c", &i3, &c3),预处理掉一个空格,然后读取到53赋值给i3,然后遇到字符'c',赋值给c3,获取输入流结束,此时输入流为" d56 e\n",第十六行代码执行结束。
173 |
174 |
175 | 执行第十七行代码scanf("%d%c", &i4, &c4),预处理掉一个空格,然后读取到'd',不进行赋值,因为第一个占位符不满足条件,获取输入流结束,i4,c4都未被赋值,此时输入流为"d56 e\n",第十七行代码执行结束。
176 |
177 |
178 | 执行第十八行代码scanf("%c", &c5),将'd'赋值给c5,获取输入流结束,此时输入流为"56 e\n",第十八行代码执行结束。
179 |
180 |
181 | 接下来输入各变量值,程序运行结束。如果后面还有scanf,那么会首先从第十八行的输入流开始获取,直到输入流队空才会再次程序暂时中止等待用户输入。
182 |
183 |
184 | putchar和gets的激活和获取输入流规则
185 |
186 |
187 | 事实上,getchar和gets可以用scanf来表示,所以getchar和gets可以视为scanf的延伸,具有scanf的性质。仅用scanf和普通C语言代码实现getchar,然后可以用getchar和普通C语言代码实现gets,分别展示如下:
188 |
189 |
190 |
191 |
192 | 得知getchar和gets的代码实现,就可以很方便地陈述它们的激活和获取输入规则了。显然,getchar的规则等同于scanf的%c规则,只是参数和返回值不一样。而gets的规则是:如果队首是换行符(回车),那么队首出队,结束获取输入流;否则,队首进行赋值然后队首出队,继续获取输入流。
193 |
194 |
195 | 篇幅略长,感谢读者能够耐心读到这里。希望这篇帖子能够对读者有所帮助~
196 |
197 |
200 |
201 |
202 |
203 |
204 |
--------------------------------------------------------------------------------
/post2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
26 |
什么是高阶函数?
27 |
使用一个或多个函数作为参数的函数称为 高阶函数。我们熟知的使用了函数作为参数的函数有min/max(使用了默认参数key,是一个函数)。例如:
28 |
29 |
当然我们也可以自己定义一个使用参数作为参数的函数,例如下面的示例代码定义了一个高阶函数:
30 |
31 |
在上面的例子中,f是一个高阶函数,第二个参数是一个函数,函数f根据函数参数的不同,调用不同的函数来对2x和x+1进行处理,h是一系列函数的抽象,g1和g2是具体的h函数。
32 |
在Python标准库和内建函数里,提供了几个非常实用的高阶函数:
33 |
map 、
34 |
reduce 、
35 |
filter 、
36 |
sorted ,本贴会详细分析一下这四个高阶函数。
37 |
38 |
map
39 |
map是内建函数。map是一个有两个参数的函数,第一个参数是函数参数func,第二个参数是可迭代数据类型。可迭代数据类型 ,即Iterable,是一切可以使用for进行迭代的数据类型,例如我们熟知的列表、字典、集合都是可迭代数据类型,它们都可以作为该函数的第二个参数。
40 |
map函数的作用是对可迭代数据类型的每一个元素x分别执行func(x),将执行后的返回值依次放进一个新的可迭代数据类型然后返回一个map实例,对该实例进行相应的类型转换可以得到可迭代数据类型。执行map函数之后,传入的可迭代数据类型参数本身不会被改变。简单地说,就是返回对可迭代数据类型的每个元素进行func操作后的结果。来看一些例子:
41 |
42 |
事实上,我们还可以用 匿名函数 对代码进行简化,匿名函数一般适用于只使用一次的函数,对于这样的函数,可以不定义具体的函数名,而直接用匿名函数表达式lambda定义其具体的操作,语法如下:
43 |
44 |
它等效于下面的语句:
45 |
46 |
lambda表达式果然非常地简单,对比之下,它将函数定义(等价语句的前两行)和调用函数参数(等价语句的第三行)直接合成了一行!我们可以用匿名函数简化上面的代码:
47 |
48 |
当然了,匿名函数可以传入的参数不仅仅是一个,也可以是0到任意多个,返回的值也跟一般的函数一样没有限制。所以,lambda函数是单行函数作函数参数的极佳替代品。来看更多例子:
49 |
50 |
上面的例子展示了匿名函数有零个参数(用了全局变量)和两个参数的例子。我们接着往下看。
51 |
reduce
52 |
reduce不是内建函数,而是标准库函数,它来自标准库functools,这意味着,你在使用之前需要使用import语句加载它。它的参数列表和返回值与map一致,同样iterable自身不会被该函数改变,但它作用的机理不同,它作用的方式是:首先对前两个元素执行func,接着将结果作为func的左参数,第三个元素作为右参数继续执行,之后对第四、第五个……同样操作,全部共执行len(iterable)-1次func。
53 |
例如,对于一个四元素iterable执行reduce(f, [x1,x2,x3,x4]),效果等效于f(f(f(x1,x2),x3),x4)。
54 |
由于它作用机理的缘故,map的函数参数是一元函数,而reduce的函数参数必须是二元函数。这点需要特别注意。并且由于reduce的机制特性,它特别适合用于处理字符串,我们来看几个例子:
55 |
56 |
可以从中看到代码量相较使用for而言简便了不少,这也体现了Python简洁的语法特性。
57 |
filter
58 |
filter是内建函数。参数和返回值同map,同样不会改变iterable自身。要求func是一元的返回布尔值的函数,当布尔值为真时,该元素会出现按原来的相对顺序在返回值里,否则该元素将会被过滤掉,不会出现在返回值里。这个函数非常好理解,来看几个例子便可:
59 |
注意filter是单词filt(过滤)的合成词,不要拼写错误成fliter。
60 |
61 |
sorted
62 |
sorted也是内建函数,虽然它不会改变传入的iterable,但它的参数、返回值与上面三个高阶函数不再一样。sorted第一个参数是iterable,第二个参数是默认参数key,传入函数参数func,可以不传,那么默认升序排序(数值或ASCII码顺序),第三个参数是默认参数reverse,是布尔值,代表是否排序完毕后进行逆序,为true进行。默认是false。函数返回值类型是列表。来看几个例子:
63 |
64 |
以上就是本篇帖子的全部内容了,希望能够对读者理解和使用Python的高阶函数有所帮助。如有错误,欢迎指出~
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/post3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
git是一款强大的版本管理工具。使用git,我们可以高效实现管理对若干文件(和文件夹)的修改状态的管理,实现版本回退、分支存储,还可以实现远程管理、多人协作这样的强悍功能。这篇帖子简要地总结了git的常用指令集合,当读者忘记了其中一些指令的时候,可以快速查阅回忆。
24 |
本帖主要分为以下四个部分:
25 |
全局设置 、
26 |
27 |
版本库 、
28 |
远程仓库 、
29 |
分支 。
30 |
31 |
32 |
全局设置
33 |
查看用户姓名和邮箱:
34 |
35 | - git config user.name
36 | - git config user.email
37 |
38 |
修改用户姓名和邮箱:
39 |
40 | - git config --global user.name "名字"
41 | - git config --global user.email "邮箱"
42 |
43 |
生成SSH:
44 |
45 | - ssh-keygen -t rsa -C "邮箱"
46 | 之后一路回车
47 |
48 |
49 |
版本库
50 |
初始化:
51 |
52 | - git init
53 | 将当前目录变成git仓库
54 |
55 |
更新:(新建的或修改的文件或文件夹,需要确认更新,从工作区放到暂存区)
56 |
57 |
58 | - git add 文件(夹)名 #可以多个参数
59 | 一次修改后add,如果继续修改,然后commit,继续修改的内容不会被提交。
60 | - git add .
61 | 上面命令会更新当前目录下的全部内容
62 |
63 |
删除:
64 |
65 | - git rm 文件名
66 | 从暂存区删除文件
67 |
68 |
提交:
69 |
70 | - git commit -m "版本信息"
71 | 将所有暂存区的提交正式保存
72 |
73 |
撤销:
74 |
75 | - git checkout -- 文件名
76 | 工作区的修改全部撤销。暂存区的不会被撤销。工作区被修改意味着你当前所做的内容被自动覆盖为暂存区内容了。
77 | - git reset HEAD 文件名
78 | 把暂存区的修改撤销掉。工作区不变,意味着你现在做的内容不会被覆盖。如果已经commit但未提交版本库,用版本回退。如果已经提交远程库,无力回天。
79 |
80 |
查询:
81 |
82 | - git status
83 | 查看有哪些文件被修改过,与commit版本不同
84 | - git lg
85 | 从近到远查看commit日志
86 | - git diff 文件名
87 | 查看与已add相比,当下的异同
88 | - git diff 版本号1 版本号2 文件(夹)
89 | 版本2为新,版本1为旧,查看更新。在分支里特别有用。记得按enter继续显示,按q退出显示
90 |
91 |
远程仓库
92 |
查询:
93 |
94 | - git remote
95 | 查看所在目录的git有多少个远程仓库
96 | - git remote -v
97 | 查看详细push,fetch权限
98 |
99 |
新建:
100 |
101 | - git remote add 远程仓库名 git@github.com:github用户名/git仓库名(.git)
102 | 如git remote add origin git@github.com:michaelliao/learngit.git
103 | - git checkout -b 本地分支名 远程仓库名/远程分支名
104 | 对分支而言的新建
105 |
106 |
删除:
107 |
108 | - git remote rm 远程仓库名
109 |
110 |
推送:
111 |
112 | - git push 远程仓库名 本地分支名
113 | 如果是第一次推送,加-u(则以后可以简写git push)
114 | 在初始化后,需要通过add,commit后才能进行push,否则会报错
115 | 第一次在github推送,会有一个警告验证key,输入yes即可
116 |
117 |
获取:
118 |
119 | - git pull 远程仓库名 本地分支名
120 | 如果获取失败,要指定本地分支和远程分支的链接,用下面二者之一:
121 |
122 | - git branch --set-upstream-to=远程仓库名/远程分支 本地分支
123 | - git fetch --all
124 | git pull 远程仓库名 本地分支名
125 |
126 |
127 |
克隆:
128 |
129 | - git clone git@github.com:用户名/git仓库名
130 | 点fork,在自己的账号下克隆了一个仓库,之后使用。会新建文件夹的,之后要cd之后才会进入
131 |
132 |
分支
133 |
查询:
134 |
135 | - git branch
136 | 带星号是当前分支
137 |
138 |
创建:
139 |
140 | - git checkout -b 分支名
141 | - git switch -c 分支名
142 | 创建并切换到新分支
143 | - git branch 分支名
144 | 创建但不切换
145 |
146 |
合并:
147 |
148 | - git merge 分支名
149 | 把某个分支合并到当前分支,如果出现冲突,必须手动解决,合并分支时一般会用fast forward模式,删除分支后丢失分支信息;如果关闭,则历史上可以看到分支信息:
150 |
151 | - git merge --no-ff -m "新commit描述" 分支名
152 |
153 |
154 |
删除:
155 |
156 | - git branch -d 分支名
157 | 删除已合并的分支。如果要删除未合并(会丢失修改)的分支,使用-D
158 |
159 |
切换:
160 |
161 | - git checkout 分支名
162 | - git switch 分支名
163 |
164 |
165 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/post4.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
对于区间上的数据查询和修改问题,我们似乎有非常多的选择。当涉及到的区间是静态区间(初始化后只查询不修改)时,我们可以根据题目性质的不同,直接使用n维前缀和/差分、ST表(稀疏表)、单调栈/单调队列得以较为简便地解决这类问题。然而,如果题目涉及了整个区间的修改操作,诸如区间每个元素加上固定值、修改为固定值、翻倍等操作时,上面的数据结构的时间复杂度便有些难堪了,我们需要一种以线性复杂度以下的复杂度进行区间修改的数据结构,没错,这便是我们今天的主角:
线段树 、
树状数组 和
块状数组。
24 |
篇幅有限,下面的示例主要例题:针对区间各元素加上常数加法操作和区间求和查询,假设题目要求每次修改和查询的时间复杂度不能是线性的时间复杂度,且空间复杂度要求为线性的。
25 |
线段树
26 |
本篇帖子是总结类的文章,所以不会涉及较多的概念引入和解释,也不会涉及基本操作证明、解析,只会给出粗略的代码,并总结相关异同特性。
27 |
线段树是二叉搜索树。叶节点代表点,其余节点代表区间。
28 |
以区间[1,4]为的四节点为例,第一层为单个节点1~4,第二层两个节点为1~2,3~4,第三层四个节点为1,2,3,4。
29 |
线段树一般用于维护大数据量、经常发生修改(一般只加,当然别的操作可以灵活变型)的数组的前缀和,以及由前缀和可以计算的任何其他值(符合结合律的,如max,min,xor),如前缀积。还可以用于模拟、DP等。而且空间经过离散化以后也可以相对压缩,所以适用范围线段树更加广一些。显然时间复杂度是nlogn。一种相似的结构,树状数组能够更好地实现该目标。
30 |
以例题为例,代码如下(相关解释见代码):
31 |
32 |
从线段树引申出来的,还有高级的线段树,如主席树(可持久化线段树)、zwk线段树、珂朵莉树,乃至于树套树等数据结构,且线段树除了可以做成二叉树之外,还可以做成三叉树等结构,在这里不做详细介绍。
33 |
树状数组
34 |
树状数组跟线段树的时间复杂度类似,但常数上时空复杂度都更优。但是相对地,代价是能维护的东西屈指可数,常见能维护的操作有:前缀和查询和单点修改、区间修改和单点查询(差分树状数组)、单点修改和区间最值、数组中位数。
35 |
特别注意,树状数组事实上并用要求的复杂度解决例题。其仅能解决例题的二者之一,即仅区间查询或仅区间修改。将其放在一起讨论仅仅是为了总结三者的异同。本题并不能使用。下面放一段解决前缀和查询和单点修改的实例代码:
36 |
37 |
块状数组
38 |
分块是一种思维,而利用这种思维可以构造出块状数组这一数据结构。将一个整体划分为若干个小块,整块整体处理,零散块单独处理。将长度为n的数组划分为a块,每块长度是n/a的下取整,区间边缘零散块单独暴力处理。根据均值不等式可以证明,a为根号n时,整块不会太少,零散块不会太多,相对最优,总体时间复杂度是根号n,是根号复杂度。
39 |
对比之下,线段树和树状数组是对数复杂度,所以分块并不会比它们更优。分块数组可以看成是高度为3的数,全体是第一层,块是第二层,元素是第三层。但是,优点在于,块状数组维护的信息不需要满足结合律,也不需要层层之间传递标记。很重要的一个补充是,同树状数组一样,块状数组更易于理解,且代码量更短,写起来相对简单。
40 |
用块状数组解决例题如下:
41 |
42 |
以上便是对线段树、树状数组和块状数组三者的简单总结。感谢读者的阅读~
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/post5.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
个人所用环境为:双系统Window10, Ubuntu20.10,使用联想拯救者电脑游戏本。自己也是一个Linux系统初学者,对这方面没有造诣,如有错误和不严谨之处,还望读者指教和海涵。
24 |
25 | 问题1:无法在C盘和D盘进行写操作
26 |
27 |
解决办法:尝试关闭window的快速启动,在window下操作,具体方法每台计算机都不一样,百度
28 |
29 | 问题2:没有ubuntu启动项
30 |
31 |
window自动更新后产生的问题
32 |
解决办法:启动bios(本系统开机时FN+F2),然后看到按钮,中间那个,选到ubuntu,然后关闭bios重启电脑即可
33 |
34 | 问题3:微信无法启动
35 |
36 |
注:Linux没有微信软件,微信是使用wine安装的
37 |
解决办法:让隐藏文件可见,用户文件夹(~)下,找到Wechat字样和.开头的文件夹,搬走,启动,(再搬回去)(不搬回去也行,影响不大),后来再次初始化的时候选一样的路径即可
38 |
39 | 问题4:typora没有菜单栏
40 |
41 |
注:typora是使用snap安装的
42 |
解决办法:这是snap的天然缺陷。不要使用snap安装,使用apt重新安装即可
43 |
44 | 问题5:typora的ctrl+5快捷键失效
45 |
46 |
注:ubuntu安装了fcitx
47 |
与fcitx快捷键冲突,点击fcitx,配置-全局配置-显示高级选项-快捷键,找到重新载入配置,换掉或删掉即可
48 |
49 | 问题6:右击菜单没有新建文本文档txt
50 |
51 |
直接在用户文件夹(路径`~`)里的模板(有的不叫这个名字,叫template)文件夹处加即可,可能需要管理员权限
52 |
53 | 问题7:vscode无法使用background-cover插件
54 |
55 |
输入下列指令即可:
56 |
57 | - sudo chown -R $(whoami) /usr/share/code
58 |
59 |
60 |
本文仅用于记录个人学习和使用linux系统的心得体会,欢迎交流~
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/post6.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
23 |
下列网站均为个人常常使用的网站,个人认为具有较好的使用价值,故分享上来。
24 |
25 | 算法题刷题网站
26 |
27 |
28 |
洛谷 推荐理由:题目多,分类全,难度和标签设置合理,有很多不同题解,有讨论区,可以查看大多数人的代码;并且提交代码大部分题目都可以查看测试点,方便对拍。另外有官方题单,非常适合跟着题单做题入门算法。
29 |
30 |
31 |
牛客网 推荐理由:国内非常权威的算法题网站,且是大多正规比赛的线上赛平台。可以查看他人的代码,基本上都有题解。有很多权威的比赛题目,如ACM(ICPC)真题。并且也有面试相关的算法题。
32 |
33 |
34 |
CodeForces 简称CF。推荐理由:经常举办比赛,适合跟着打比赛(虽然都是大晚上的比赛),难度比较适合,思维类题目较多,对有意向参加ACM的人有很大帮助。
35 |
36 |
37 |
Leetcode 又称力扣。推荐理由:算法题目多与工作面试题相关,适合准备面试的人准备。
38 |
39 |
40 | 知识汇总网站
41 |
42 |
43 |
oi-wiki 推荐理由:汇集了非常全面的算法知识的百科网站。里面能够找到大量算法知识。适合系统学习算法知识参考,也适合当作百科词条查阅。
44 |
45 |
46 |
菜鸟教程 推荐理由:主要是前端和后端知识汇总,与Web相关,有HTML、CSS、JS等大量知识,非常详细,适合初学者翻看,也适合跟着系统学习。
47 |
48 |
49 | 交流网站
50 |
51 |
52 |
stackoverflow 推荐理由:编程问题有求必应(虽然是英文社区,需要英文功底),只要检索/提问能力较强,再偏僻、刁钻、细节的问题也有可能有人可以解答。
53 |
54 |
55 |
希望上述网站能够在读者遇到问题时帮到读者,如果有别的精彩的网站也欢迎交流分享~
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/posts.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |