diff --git a/省选准备/assets/day10/Splay.cpp b/省选准备/assets/day10/Splay.cpp new file mode 100644 index 0000000..0cc2cfd --- /dev/null +++ b/省选准备/assets/day10/Splay.cpp @@ -0,0 +1,195 @@ +#include +#include +#define sz(x) (x == 0 ? 0 : x->size) +#define update(x) \ + if (x) x->size = sz(x->child[0]) + sz(x->child[1]) + 1 +#define getVal(x) (x == 0 ? 0 : x->val) +typedef int valType; +const int arrSize = 100005; +struct Node +{ + valType val; + int size; + Node *child[2], *father; + Node *Init(int x, Node *f) + { + val = x, size = 1, father = f, child[0] = child[1] = 0; + return this; + } +}; +typedef Node *lpNode; +template +struct allocator +{ +private: + int i; + _ty *data; + _typ *pt; + +public: + allocator(int size) + { + data = new _ty[size]; + pt = new _typ[size]; + this->i = 0; + for (int j = 0; j < size; j++) pt[j] = &data[j]; + } + _typ alloc() + { + return pt[i++]; + } + void free(_typ ptr) + { + pt[--i] = ptr; + } +}; +struct SplayTree +{ +private: + lpNode root = 0; + allocator *mem; + void rotate(lpNode x) + { + if (x == 0 || x->father == 0) return; + int d = (x == x->father->child[0]); + lpNode y = x->father; + y->child[d ^ 1] = x->child[d]; + if (x->child[d]) x->child[d]->father = y; + x->father = y->father; + if (y->father) y->father->child[y == y->father->child[1]] = x; + y->father = x; + x->child[d] = y; + update(y); + update(x); + } + void Splay(lpNode x, lpNode &target) + { + lpNode targetFather = target->father; + while (x->father != targetFather) + if (x->father == target) + rotate(x); + else if ((x->father->father->child[0] == x->father) == (x->father->child[0] == x)) + rotate(x->father), rotate(x); + else + rotate(x), rotate(x); + target = x; + } + lpNode find(int x) + { + lpNode s = root; + while (true) + if (s == 0) + return 0; + else if (s->val == x) + { + Splay(s, root); + return s; + } + else + s = s->child[x > s->val]; + } + lpNode join(lpNode x, lpNode y) + { + if (x == 0) + return y; + else + { + lpNode m = max_min(x, 1); + Splay(m, x); + m->child[1] = y; + if (y) y->father = m; + update(m); + return x; + } + } + +public: + SplayTree(int _) + { + mem = new allocator(_); + } + lpNode max_min(lpNode x, int i) + { + while (x && x->child[i]) x = x->child[i]; + return x; + } + void insert(int x) + { + if (root == 0) + root = mem->alloc()->Init(x, 0); + else + { + lpNode s = root, p = 0; + while (s) + p = s, s = s->child[x > s->val]; + s = mem->alloc()->Init(x, p); + p->child[x > p->val] = s; + Splay(s, root); + } + } + void remove(int x) + { + lpNode p = find(x); + if (p) + { + root = join(p->child[0], p->child[1]); + root->father = 0; + mem->free(p); + } + } + int rank(int x) + { + lpNode s = root; + int ans = 0; + while (s) + if (x <= s->val) + s = s->child[0]; + else + ans = ans + 1 + sz(s->child[0]), s = s->child[1]; + return ans + 1; + } + lpNode select(int x) + { + lpNode s = root; + while (s) + if (x == sz(s->child[0]) + 1) + return s; + else if (x <= sz(s->child[0])) + s = s->child[0]; + else + x = x - 1 - sz(s->child[0]), s = s->child[1]; + } + lpNode near(int x, int d) + { + lpNode s = root, ans = 0; + while (s) + if (d ? s->val > x : s->val < x) + ans = s, s = s->child[d ^ 1]; + else + s = s->child[d]; + return ans; + } +}; +int main() +{ + SplayTree st(arrSize); + int n, op, num; + scanf("%d", &n); + for (int i = 0; i < n; i++) + { + scanf("%d%d", &op, &num); + if (op == 1) + st.insert(num); + else if (op == 2) + st.remove(num); + else if (op == 3) + printf("%d\n", st.rank(num)); + else if (op == 4) + printf("%d\n", getVal(st.select(num))); + else if (op == 5) + printf("%d\n", getVal(st.near(num, 0))); + else if (op == 6) + printf("%d\n", getVal(st.near(num, 1))); + } + return 0; +} diff --git a/省选准备/assets/day10/Treap.cpp b/省选准备/assets/day10/Treap.cpp new file mode 100644 index 0000000..075a408 --- /dev/null +++ b/省选准备/assets/day10/Treap.cpp @@ -0,0 +1,150 @@ +#include +#include +#define sz(x) ((x) == 0 ? 0 : (x)->sz) +template +struct Allocator +{ + int idx; + T *data; + T **ptr; + Allocator(int size) + { + idx = 0; + data = new T[size]; + ptr = new T *[size]; + for (int i = 0; i < size; i++) ptr[i] = &data[i]; + } + ~Allocator() + { + delete[] data; + delete[] ptr; + } + T *alloc() { return ptr[idx++]; } + void free(T *pt) { ptr[--idx] = pt; } +}; +struct Treap +{ + typedef struct Node + { + int val, pri, sz; + Node *ch[2]; + void Init(int x = 0) + { + pri = rand(); + ch[0] = ch[1] = 0; + val = x; + sz = 1; + } + void update() + { + sz = 1 + sz(ch[0]) + sz(ch[1]); + } + } * lpNode; + Treap(int max_size, int _seed) + { + srand(_seed); + alloc = new Allocator(max_size); + root = 0; + } + void ins(int x) + { + insert(root, x); + } + void del(int x) + { + remove(root, x); + } + int rnk(int x) + { + int ans = 1; + lpNode cur = root; + while (cur) + if (x <= cur->val) + cur = cur->ch[0]; + else + ans += sz(cur->ch[0]) + 1, cur = cur->ch[1]; + return ans; + } + int kth(int x) + { + lpNode cur = root; + while (cur) + if (x == sz(cur->ch[0]) + 1) + return cur->val; + else if (x <= sz(cur->ch[0])) + cur = cur->ch[0]; + else + x -= sz(cur->ch[0]) + 1, + cur = cur->ch[1]; + return 0; + } + +private: + Allocator *alloc; + lpNode root; + void rotate(lpNode &node, int d) + { + if (node == 0) return; + lpNode ch = node->ch[d ^ 1]; + if (ch) + { + node->ch[d ^ 1] = ch->ch[d]; + ch->ch[d] = node; + node = ch; + node->ch[d]->update(); + node->update(); + } + } + void insert(lpNode &node, int x) + { + if (node == 0) + (node = alloc->alloc())->Init(x); + else + { + int cmp = x < node->val; + insert(node->ch[cmp ^ 1], x); + node->update(); + if (node->ch[cmp ^ 1]->pri < node->pri) rotate(node, cmp); + } + } + void remove(lpNode &node, int x) + { + if (node == 0) + return; + else + { + if (x < node->val) + remove(node->ch[0], x); + else if (x > node->val) + remove(node->ch[1], x); + else if (node->ch[0] == 0) + node = node->ch[1]; + else if (node->ch[1] == 0) + node = node->ch[0]; + else + { + int d = node->ch[0]->pri < node->ch[1]->pri; + rotate(node, d); + remove(node->ch[d], x); + } + if (node) node->update(); + } + } +}; +int main() +{ + Treap T(100005, 3224); + int n; + scanf("%d", &n); + for (int i = 0, op, x; i < n; i++) + { + scanf("%d%d", &op, &x); + if (op == 1) T.ins(x); + if (op == 2) T.del(x); + if (op == 3) printf("%d\n", T.rnk(x)); + if (op == 4) printf("%d\n", T.kth(x)); + if (op == 5) printf("%d\n", T.kth(T.rnk(x) - 1)); + if (op == 6) printf("%d\n", T.kth(T.rnk(x + 1))); + } + return 0; +} \ No newline at end of file diff --git a/省选准备/省选基础算法.pdf b/省选准备/省选基础算法.pdf index 184ec6b..375f480 100644 Binary files a/省选准备/省选基础算法.pdf and b/省选准备/省选基础算法.pdf differ diff --git a/省选准备/省选基础算法.tex b/省选准备/省选基础算法.tex index 9e4f215..46cd80d 100644 --- a/省选准备/省选基础算法.tex +++ b/省选准备/省选基础算法.tex @@ -334,7 +334,7 @@ Manacher 模板题 \paragraph{练习题} \subparagraph{\href{http://poj.org/problem?id=1284}{POJ1284} - Primitive Roots} 如果$n\in\mathbb{N_+}$有一个原根,那么$n$一共有$\varphi(\varphi(n))$个不同余的原根 \codeinput[Primitive Roots]{assets/day3/poj1284.cpp} -\section{数据结构} +\section{数据结构 I} \subsection{树状数组} 在$\lg{n}$的时间内更新或查询$\sum\limits_{i=1}^n a_i$ \paragraph{lowbit} \verb|lowbit(x) = x & -x| \paragraph{两个操作} $\lg{n}$更新或查询 @@ -492,7 +492,7 @@ Manacher 模板题 \subsection{最小费用最大流} 每次找到一条最短的路增广,大体上与Dinic完全相同,不过总感觉很奇怪,包括没有了cur,多出了vis等一些不同,并且不加就会爆炸。 \codeinput[\href{http://poj.org/problem?id=2135}{POJ2135} - Farm Tour]{assets/day6/poj2135.cpp} \paragraph{后记} 我多次尝试直接移植dinic的模板来解决最小费用最大流问题,不过最后都只是收获了一个个RE,看来直接照抄dinic的代码是不行的,仍然要加上vis数组之类的东西来保证不会爆栈(虽然我觉得我的移植代码也不会爆栈),如果哪名巨佬在Review我的代码时发现了方法,请不吝告知,蒟蒻万分感谢。 -\section{XJB算法合集1} XJB算法的时间复杂度均为$O(\mbox{跑得过})$,空间复杂度均为$)(\mbox{开的下})$。 +\section{XJB算法合集 I} XJB算法的时间复杂度均为$O(\mbox{跑得过})$,空间复杂度均为$)(\mbox{开的下})$。 \subsection{二分与三分} \paragraph{二分} 玄学算法之一,什么都可以往上套,会使时间复杂度乘上$\log{n}$。但是二分法仅适用于求解具有单调性的问题,所以做题前要大胆猜想,小(bu)心(yong)求证。 \paragraph{二分的写法} @@ -555,7 +555,7 @@ Manacher 模板题 \section{分治} 把问题分割成更小的子问题递归求解,再处理不同子问题之间的部分,这种算法设计方法就是分治法。关于分治类算法的时间复杂度,算法导论上详细的介绍了专有结论:主定理。在此不再赘述。 \subsection{序列上的分治} 数列的逆序对数除了使用数据结构外,也可以在归并排序的基础上统计额外信息来求得。\\ - 假设我们现在要统计数列 A 中逆序对的个数。如图所示,我们可以将数列$A$平均分成两半得到数列$B$和数列 C,于是,数列 A 中的逆序对 ) , ( j i 必然是下面三种之一:\\ + 假设我们现在要统计数列 A 中逆序对的个数。如图所示,我们可以将数列$A$平均分成两半得到数列$B$和数列$C$,于是,数列$A$中的逆序对$(i,j)$必然是下面三种之一:\\ (1)$i,j$都属于数列$B$的逆序对$(i,j)$\\ (2)$i,j$都属于数列$C$的逆序对$(i,j)$\\ (3)$i$属于数列$B$而$j$属于数列$C$的逆序对$(i,j)$\\ @@ -571,4 +571,23 @@ Manacher 模板题 \codeinput[\href{http://acm.hdu.edu.cn/showproblem.php?pid=1007}{HDU1007} - Quoit Design]{assets/day8/hdu1007.cpp} \subsection{树分治} 暂时跳过 \subsection{cdq分治} 暂时跳过 +\section{数据结构 II} +\subsection{平衡树} +\subsubsection{前导知识} +\paragraph{二叉搜索树} 假定我们都知道什么是二叉搜索树。 +\paragraph{二叉搜索树的旋转} 假定我们都知道如何左旋和右旋。 +\paragraph{算法时间复杂度分析初步} 没有必要的知识。 +\subsubsection{Splay} +\paragraph{简介} 一种通过Splay操作使\textbf{均摊}时间复杂度为$\log{n}$的二叉搜索树。 +\paragraph{Splay操作} 将一个点移动到它上方的指定位置。 +\subparagraph{节点$x$的父节点$y$是根节点} 将$x$通过左旋或右旋转到根。结束。 +\subparagraph{节点$x$的父节点$y$不是根节点,$y$的父节点为$z$,且$x$与$y$同时是各自父节点的左孩子或者同时是各自父节点的右孩子} 先将$y$转上去,再将$x$转上去。 +\subparagraph{节点$x$的父节点$y$不是根节点,$y$的父节点为$z$,且$x$与$y$一个是其父节点的左孩子而另一个是其父节点的右孩子} 把$x$转上去两次。 +\paragraph{示例代码} 模板题 +\codeinput[\href{http://www.lydsy.com/JudgeOnline/problem.php?id=3224}{BZOJ3224} - 普通平衡树]{assets/day10/Splay.cpp} +\subsubsection{Treap} +\paragraph{简介} 权值满足堆性质,\textbf{期望}时间复杂度为$\log{n}$的二叉搜索树。 +\paragraph{做法} 插入之后维护一下堆性质,乱搞搞就好了,递归的方法写起来很容易,并且不易写错。删除的时候就把那个节点转到一条链上,然后删掉它。 +\paragraph{示例代码} 模板题 +\codeinput[\href{http://www.lydsy.com/JudgeOnline/problem.php?id=3224}{BZOJ3224} - 普通平衡树]{assets/day10/Treap.cpp} \end{document} \ No newline at end of file