版权声明:如需转载请标明出处,未得到本人许可请勿转载。
今天就可以看到传说中的
数据结构
嘿嘿嘿嘿
都有什么呢
链表
队列
栈
st表
hash
线段树
树链剖分
一、栈:
放出来这个看烂了的图
值得一提的是栈的性质是先进后出!
那么:
简单粗暴直接上代码
int main() { r=; while (Q--) { cin>>A; ) { cin>>B; s[++r]=B; m[r]=max(m[r-],s[r]); c[r]=c[r-]+s[r]; mc[r]=max(mc[r-],c[r]); minc[r]=min(minc[r-],c[r]); sum+=B; } ) {r--; sum-=s[r]}; ) cout<<m[r]<<endl; ) cout<<mc[r]<<endl; ) cout<<sum-minc[r]<<endl; } ; }
那么另一个更难一点的题
首先我们需要分析这个题。在光标的后面插入一个数字或者是删除光标签的最后一个数字都是栈的方式。左移和右移光标就比较值得分析,当光标在最左边或者是最右边的时候,光标左移就是不动的,当光标在最右边的时候,也是不动的,为了方便我们来求,可以把这个栈复制一遍放到后面,但并不合并,看成两个栈,栈尾接栈头那种,光标左移,左边的出栈,右边入栈,右移,左边入栈,右边出栈。求前缀和一个思路就是在入栈的时候直接加个sum求和就行。
那么下一道:
恩……这个题是一道dp题,结合了数据结构写得dp,是一道区间dp,首先需要知道的是,这个题可以枚举出栈序列最后一个是什么,有n个数,最后一个出栈的是k,(1,k-1)比(k+1,n)先出栈。
那么就需要解一下这个区间dp的情况,dp[l][r]指l~r这些数之间的方案总数。k∈(l,r)
dp[l][r] += Σdp[1][k-1]*dp[k+1][r]
ans = dp[1][n]
可以考虑枚举最后一个出栈的元素。分成两个相互独立的子问题,用DP就好
二、队列
继续按照规则,上一张特别特别老的图
请不要在意一些奇奇怪怪的边
那么队列的性质,先进先出
既然是队列,就要考虑所谓的front和rear,所以在队列中会有两个双指针,一个指向队首,一个队尾
但是实际上,队首是不能在队尾之后的,为了方便我们可以有循环队列。
那就来一道题愉快愉快吧
#include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; s[] int main() { l=; r=; while (Q--) { cin>>A; ) { cin>>B; s[++r]=B; } ) l++; ) { MAX=; for (i=l; i<=r; i++) MAX=max(MAX,s[i]); cout<<MAX<<endl; } } ; }
然后就可以讲一下单调队列了
这个可能拿函数的增减性会更好理解一些,在每次入队的时候,判断当前元素与队尾的大小关系,若小于队尾元素,则队尾不可能成为最小值,直接删除就好,但是如果要求最大值,直接输出队首元素即可,由于每个元素至多进队出队1次,所以就有时间复杂度:O(n)。
单调队列中都是有可能成为最大值的数 第i次插入的一定是第i次删除的。 O(Q) #include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; s[] int main() { l=; r=; Q Ql=; Qr=; while (Q--) { cin>>A; ) { cin>>B; while (Ql<=Qr && Q[Qr]<=B) Qr--; Q[++Qr]=B; cnt++; P[Qr]=cnt; } ) { CNT++; if (P[Ql]==CNT) Ql++; } ) cout<<Q[Ql]<<endl; } ; } max{a1-x,a2-x,..,an-x} max{a1,a2,..,an}-x
注意3738行的解释,这里每次把这个值减去x求所有数的最大值和先求出所有的最大值再减去x是一样的,还可以省去一个循环,小优化?
37 max{a1-x,a2-x,..,an- x} 38 max{a1,a2,..,an}-x
那就来一道题冷静一下吧
#include <cmath> #include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; s[] int main() { l=; r=; Q Ql=; Qr=; while (Q--) { cin>>A; ) { cin>>B; sum+=B; while (Ql<=Qr && Q[Qr]<=sum) Qr--; Q[++Qr]=sum; cnt++; P[Qr]=cnt; a[cnt]=sum; } ) { CNT++; SUM+=a[CNT]; if (P[Ql]==CNT) Ql++; } ) cout<<Q[Ql]-SUM<<endl; } ; }
手打一道题
广告印刷:
直接把模板拿出来
有n个数ai。从中选出一段区间[L,R],使得(R-L+1)*min{a-L…………a-R}最大,n<=1000000
考虑对于第i个数,求出当这个数成为最小值时,往左往右分别最远能到哪里。因此来更新答案,那就可以用单调队列来实现。
那么代码实现:
三、链表
不会做,没有代码就不写了。
四、ST表
不会做,没有代码就不写了。
(博主贼菜,毛都不会)
·
五、链上最值问题
给定一棵树,每次询问两个点x、y,求这两个点的路径上最长的边是多少。
怎么做?首先逐步分析,令F[i][j]表示i向上跳 2j 能跳到哪。
g[i][j]表示向上跳的过程中遇见的最长的边是多少。
如何求g?
那么就需要求一下两个点的LCA(最近公共祖先)
用倍增的方法求出x到LCA的最长边,y到LCA的最长边
所以最大值就可以表示出来了。
f[i][] g[i][] g表示从i出发向上跳2^j步的最小值 ; j<=; j++) ; i<=n; i++) { f[i][j]=f[f[i][j-]][j-]; g[i][j]=min(g[i][j-], g[f[i][j-]][j-]); } x,LCA k=deep[x]-deep[LCA] ; i>=; i--) <<i)) { MIN=min(MIN,g[x][i]); k-=(<<i); x=f[x][i]; } sum[x]=从根出发到x 的长度之和 sum[x]+sum[y]-*sum[LCA];
代码有点多,可能会出现问题。
20,22就是指链上和问题
链上抑或和问题呢
这几个题都是一个类型,都需要求LCA。
Hash?线段树?
这些东西真的放一块好么?
(这里特别感谢张浩威老师的课件以及讲解,瞎几把写写,有问题请及时提出)
(线段树、链表、ST表这些都是非常重要的数据结构的知识点,但是博主并没有很掌握,可能得过段时间再更新了,清北学堂一天讲的东西还是很懵逼的,Day1还是和蔼可亲,Day2直接开始特别难什么马拉车的,Day3dp,博客也写的贼水,Day4也才刚刚写完,Day5又讲懵逼了一天,讲的图论,Day6又是数论,但愿这几天不要白费)