============数据结构模板================ 动态树 动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作。其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是LCT(link-cut tree)。 const int MAXN = 100010; struct Node { Node *ch[2], *p; int size, value; bool rev; Node(int t = 0); inline bool dir(void) { return p->ch[1] == this; } inline void SetC(Node *x, bool d) { ch[d] = x; x->p = this; } inline void Rev(void) { swap(ch[0], ch[1]); rev ^= 1; } inline void Push(void) { if (rev) { ch[0]->Rev(); ch[1]->Rev(); rev = 0; } } inline void Update(void) { size = ch[0]->size + ch[1]->size + 1; } } Tnull, *null = &Tnull, *fim[MAXN]; // 要记得额外更新null的信息 Node::Node(int _value) { ch[0] = ch[1] = p = null; rev = 0; } inline bool isRoot(Node *x) { return x->p == null || (x != x->p->ch[0] && x != x->p->ch[1]); } inline void rotate(Node *x) { Node *p = x->p; bool d = x->dir(); p->Push(); x->Push(); if (!isRoot(p)) p->p->SetC(x, p->dir()); else x->p = p->p; p->SetC(x->ch[!d], d); x->SetC(p, !d); p->Update(); } inline void splay(Node *x) { x->Push(); while (!isRoot(x)) { if (isRoot(x->p)) rotate(x); else { if (x->dir() == x->p->dir()) { rotate(x->p); rotate(x); } else { rotate(x); rotate(x); } } } x->Update(); } inline Node* Access(Node *x) { Node *t = x, *q = null; for (; x != null; x = x->p) { splay(x); x->ch[1] = q; q = x; } splay(t); //info will be updated in the splay; return q; } inline void Evert(Node *x) { Access(x); x->Rev(); } inline void link(Node *x, Node *y) { Evert(x); x->p = y; } inline Node* getRoot(Node *x) { Node *tmp = x; Access(x); while (tmp->Push(), tmp->ch[0] != null) tmp = tmp->ch[0]; splay(tmp); return tmp; } // 一定要确定x和y之间有边 inline void cut(Node *x, Node *y) { Access(x); splay(y); if (y->p != x) swap(x, y); Access(x); splay(y); y->p = null; } inline Node* getPath(Node *x, Node *y) { Evert(x); Access(y); return y; } inline void clear(void) { null->rev = 0; null->sie = 0; null->value = 0; } splay树模板,分为两个部分,第一个部分为splay当作平衡树使用,第二个部分为splay当作线段树维护序列使用。注意在每次Insert之后都要splay其返回值到根 // 最大可能的节点数 const int MAXN = 100010; // 每个打标记的操作就是更新这个节点的信息,然后对子节点打标记 struct Node { Node *ch[2], *p; int size, value; bool rev; inline bool dir(void) { return p->ch[1] == this; } inline void SetC(Node *x, bool d) { ch[d] = x; x->p = this; } inline void Rev(void) { swap(ch[0], ch[1]); rev ^= 1; } // null永远不会push inline void Push(void) { if (rev) { ch[0]->Rev(); ch[1]->Rev(); rev = 0; } } // null永远不会update inline void Update(void) { size = ch[0]->size + ch[1]->size + 1; } inline void initInfo(void) { rev = 0; } } Tnull, *null = &Tnull, *data, POOL[MAXN]; class Splay { public: Node *root; inline void rotate(Node *x) { Node *p = x->p; bool d = x->dir(); p->Push(); x->Push(); p->p->SetC(x, p->dir()); p->SetC(x->ch[!d], d); x->SetC(p, !d); p->Update(); } inline void splay(Node *x, Node *G) { if (G == null) root = x; while (x->p != G) { if (x->p->p == G) rotate(x); else { if (x->dir() == x->p->dir()) { rotate(x->p); rotate(x); } else { rotate(x); rotate(x); } } } x->Push(); x->Update(); } inline Node* Renew(int value) { Node *ret = data++; ret->ch[0] = ret->ch[1] =ret->p = null; ret->size = 1; ret->value = value; ret->initInfo(); return ret; } inline Node* getMin(Node *x) { Node *tmp = x; while (tmp->ch[0] != null) tmp = tmp->ch[0]; return tmp; } inline Node* getMax(Node *x) { Node *tmp = x; while (tmp->ch[1] != null) tmp = tmp->ch[1]; return tmp; } // 查询第k大 inline Node* getKth(int k) { Node *tmp = root; assert(k > 0 && k <= root->size); while (true) { tmp->Push(); if (tmp->ch[0]->size + 1 == k) return tmp; if (tmp->ch[0]->size >= k) tmp = tmp->ch[0]; else k -= tmp->ch[0]->size + 1, tmp = tmp->ch[1]; } } // 以下为splay当作平衡树使用 // 查找树中value = v的元素, 返回之后splay inline Node* find(int v) { Node *tmp = root; while (tmp != null) { tmp->Push(); if (tmp->value == v) return tmp; if (v < tmp->value) tmp = tmp->ch[0]; else tmp = tmp->ch[1]; } return null; } // 统计有多少元素小于等于v, 当flag = 1时,统计多少元素严格小于v, 一定要记得splay最后的那个tmp inline int Count(int v, bool flag = 0) { Node *tmp = root, *last = null; int ret = 0; while (tmp != null) { tmp->Push(); last = tmp; if ((!flag && tmp->value > v) || (flag && tmp->value >= v)) { tmp = tmp->ch[0]; } else ret += tmp->ch[0]->size + 1, tmp = tmp->ch[1]; } if (last != null) splay(last, null); return ret; } // 删除x这个结点 inline void erase(Node* x) { splay(x, null); if (x->ch[0] == null || x->ch[1] == null) { int d = x->ch[1] != null; root = x->ch[d]; root->p = null; return; } Node *L = getMax(x->ch[0]), *R = getMax(x->ch[1]); splay(L, x); splay(R, x); L->SetC(R, 1); L->p = null; root = L; L->Update(); } // 插入一个值为value的节点,初始要以Insert(root, null, value)来调用, 返回之后splay inline Node* Insert(Node *&now, Node* father, int value) { if (now == null) { now = Renew(value); now->p = father; return now; } Node *ret; now->Push(); if (value <= now->value) ret = Insert(now->ch[0], now, value); else ret = Insert(now->ch[1], now, value); now->Update(); return ret; } // 以下为splay维护序列, 初始要在原序列中放入一个-inf和inf来防止边界条件 // 得到原数列中[l,r]区间对应的结点,如果l == r + 1则表示是一个空区间 inline Node* getInterval(int l, int r) { assert(l <= r + 1); Node *L = getKth(l), *R = getKth(r + 2); splay(L, null); splay(R, L); return R->ch[0]; } // 删除一段区间[l,r] inline void eraseInterval(int l, int r) { getInterval(l, r); root->ch[1]->ch[0] = null; root->ch[1]->Update(); root->Update(); } // 在位置l的后面插入一段区间x (0 <= l <= n) inline void insertInterval(int l, Node *x) { Node *L = getKth(l + 1), *R = getKth(l + 2); splay(L, null); splay(R, L); R->SetC(x, 0); R->Update(); L->Update(); } // 把数列a的[l,r]构建为一个splay inline Node* Build(int l, int r, int a[]) { if (l > r) return null; int mid = (l + r) >> 1; Node *ret = Renew(a[mid]); if (l == r) return ret; ret->SetC(Build(l, mid - 1, a), 0); ret->SetC(Build(mid + 1, r, a), 1); ret->Update(); return ret; } } T; void clear(void) { data = POOL; T.root = null; } 倍增算法构建后缀数组,时间复杂度为 $O(n \lg n)$. 注意字符串和最终的后缀数组均从 1 开始编号. 要保证字符串中都为大于0的字符, 而且字符串的第n+1位应该为0. // string is 1-base, sa is 1-base int w[MAXM]; inline void Sort(int a[], int ret[], int n, int m = MAXM - 1) { for (int i = 0; i <= m; i++) w[i] = 0; for (int i = 1; i <= n; i++) w[a[i]]++; for (int i = 1; i <= m; i++) w[i] += w[i - 1]; for (int i = n; i >= 1; i--) ret[w[a[i]]--] = i; } int wa[MAXN], wb[MAXN], tmp[MAXN]; inline void getSA(int ch[], int sa[], int n) { int *x = wa, *y = wb; for (int i = 1; i <= n; i++) x[i] = ch[i]; Sort(ch, sa, n); for (int j = 1, p = 1, m = MAXN - 1; p < n; m = p, j <<= 1) { p = 0; for (int i = n - j + 1; i <= n; i++) y[++p] = i; for (int i = 1; i <= n; i++) if (sa[i] > j) y[++p] = sa[i] - j; for (int i = 1; i <= n; i++) tmp[i] = x[y[i]]; Sort(tmp, sa, n, m); for (int i = 1; i <= n; i++) sa[i] = y[sa[i]]; swap(x, y); x[sa[1]] = p = 1; for (int i = 2; i <= n; i++) { if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = p; else x[sa[i]] = ++p; } } sa[0] = n + 1; // for calculate height. } int rank[MAXN]; inline void getHeight(int ch[], int sa[], int height[], int n) { for (int i = 1; i <= n; i++) rank[sa[i]] = i; for (int i = 1, t = 0; i <= n; i++) { if (t > 0) t--; while (ch[i + t] == ch[sa[rank[i] - 1] + t]) t++; height[rank[i]] = t; } } 构建后缀自动机。统计一个串出现的次数时,只需统计其节点所对应的子树中,end为true的节点的个数即可。将所有节点按val值从小到大排序后即可得到parent树由根开始的BFS序。 struct Node { Node *next[26], *par; int val, end; // 26 is volatile } POOL[MAXN << 1], *data, *root, *last; //Note that the size of POOL should be doubled. inline void Add(int x) { Node *p = last, *np = data++; np->val = p->val + 1; np->end = true; while (p && !p->next[x]) p->next[x] = np, p = p->par; if (p == 0) { np->par = root; } else { Node *q = p->next[x]; if (q->val == p->val + 1) { np->par = q; } else { Node *nq = data++; nq->val = p->val + 1; memcpy(nq->next, q->next, sizeof q->next); nq->par = q->par; np->par = q->par = nq; while (p && p->next[x] == q) p->next[x] = nq, p = p->par; } } last = np; } void Clear(void) { data = POOL; last = root = data++; }