mirror of
https://github.com/Kiritow/OJ-Problems-Source.git
synced 2024-03-22 13:11:29 +08:00
457 lines
11 KiB
Plaintext
457 lines
11 KiB
Plaintext
============数据结构模板================
|
||
|
||
动态树
|
||
动态树是一类要求维护森林的连通性的题的总称,这类问题要求维护某个点到根的某些数据,支持树的切分,合并,以及对子树的某些操作。其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构就是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++;
|
||
}
|
||
|