OJ-Problems-Source/.ACM-Templates/TXTs/数据结构模板.txt
2016-11-22 09:38:35 +08:00

457 lines
11 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

============数据结构模板================
动态树
动态树是一类要求维护森林的连通性的题的总称这类问题要求维护某个点到根的某些数据支持树的切分合并以及对子树的某些操作。其中解决这一问题的某些简化版不包括对子树的操作的基础数据结构就是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++;
}