先推荐一下codevs网站,网址 http://www.codevs.cn,之前noip大多数都是在这里面做题的
codeforces有空的,也可以尝试一下,不过貌似都是英文的 http://codeforces.com
下面进入正题,NOIP所需要掌握的数据结构,下面的难度不一,但是都需要掌握,线段树以及树状数组会单独列出
- 线段树
- 线段树,是一个完全二叉树。支持区间修改,区间查询,单点修改,单点查询,多用于一段连续序列进行多次查询,多次修改。但是,要注意连续二字。必须是一串数字,而不能是断开的。除此,线段树的空间是原数组的4倍。
- 单点修改,区间查询:(修改)分治思想分到l==r开始往上更新,只往更新后答案改变的那一边更新。
- 区间修改,区间查询:注意pushdown操作,若当前区间被需要修改的区间的区间完全覆盖,那么标记好,并把当前线段树节点赋好值。查询时需要下放标记
- 树状数组
- 树状数组,与线段树作用差不多,相比之下要更好实现,而且常数要更小些。不过要注意树状数组的数组大小至少是原数组的2倍。对于实现单点修改,区间查询是很容易的,但如果要实现区间修改,区间查询,那么就要运用公式了。
- 区间树状数组:
第一步 : 输入 a[i] ,新起另一个数组 d[i]存 a[i]-a[i-1] 。
第二步:以 d[i] 为基础构造c[i] 树状数组,与普通树状数组的第一步一样。
第三步:当进行区间修改时,设输入区间为[l,r], 增加量为sd[l]=d[l]+s;
D[r+1]=d[r]-s;
第四步:当求单点值时, a[i]=d[1]+d[2]+…..+d[i];
第五步:当求区间值时,第三步时对所有改变的 d[i] 依照普通树状数组改变 c[i]的值,最后依照普通树状数组,c[r]-c[l] 及可求值,我对普通树状数组并不熟,尤其是第二步和第三步
- 树状数组修改一个结果后,若编号为 x ,则x+x&(-x) 及其所求的父亲节点
x-x&(-x) 及其所求的区间和
(r-l+1)*(d[1]+…+d[l-1])+(r+1)(d[l]+…+d[r])-(l*d[l]+(l+1)*d[l+1]+…r*d[r])
- ST表
- 时间复杂度:建表O(nlogn),查询O(1)。
- st表目前最好用的地方就是求rmq,虽然线段数也可以做到时间复杂度和st表差不太多,但对于多询问的话还是用st表来解题。主要分为两个部分:
预处理:dp思想倍增思想,用f[i][j]表示在(i,i+(1<<j)-1)这段区间的最值,所以由此可见f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
查询:dp思想,对于区间[l,r]的最值,由于根据f[i][j],先找出最大的k,满足2^k<=r-l+1,为啥是这样呢,根据先前f[i][j]的定义,k就是f数组的第二维,也就是倍增的次数,那么f[l][k]就表示了在所求区间内的(l,l+(1<<k)-1)这段区间。可以看出(l+(1<<k)-1)明显比r小,所以所求的最值就是max(f[l][k],f[r-(1<<k)+1][k]), f[r-(1<<k)+1][k]的意思是指区间(r-(1<<k)+1,r),从此可以看出两个区间虽有少量重复,但将[l,r]这段区间完全覆盖了。(问题:空间复杂度太大,如何优化空间复杂度)解:其实判断错误虽为二维,但k表示(1<<k),一般不会大于20,所以还是很划算的。
- 栈
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。很简单自己用系统栈或是用数组模拟即可。
- 优先队列
优先队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。优先队列其实就是堆,考试中一般用的是系统堆。
bool operator<const(node &a)const{ return a.dis<dis;};
priority_queue<node> q;
小根堆