樹論
LCT
class C_LinkCutTree
{
public:
int rev[N], fa[N], ch[N][2];
/*====================*/
bool Which(int x)
{
return ch[fa[x]][1] == x;
}
bool IsRoot(int x)
{
return ch[fa[x]][Which(x)] != x;
}
/*====================*/
void PushUp(int x)
{
/*PushUp*/
}
void PushAll(int x)
{
if (!IsRoot(x))
{
PushAll(fa[x]);
}
PushDown(x);
}
void PushDown(int x)
{
if (rev[x])
{
swap(ch[x][0], ch[x][1]);
rev[ch[x][0]] ^= 1;
rev[ch[x][1]] ^= 1;
rev[x] = 0;
}
/*PushDown*/
}
/*====================*/
void Rotate(int x)
{
int y = fa[x], z = fa[y], w = Which(x);
if (!IsRoot(y)) ch[z][Which(y)] = x; fa[x] = z;
ch[y][w] = ch[x][w ^ 1];
if (ch[y][w]) fa[ch[y][w]] = y;
ch[x][w ^ 1] = y; fa[y] = x;
PushUp(y); PushUp(x);
}
void Splay(int x)
{
PushAll(x);
for (; !IsRoot(x); Rotate(x))
{
if (!IsRoot(fa[x]))
{
Rotate(Which(x) == Which(fa[x]) ? fa[x] : x);
}
}
}
/*====================*/
void Access(int x)
{
for (int p = 0; x; p = x, x = fa[x])
{
Splay(x), ch[x][1] = p, PushUp(x);
}
}
/*====================*/
int FindRoot(int x)
{
Access(x); Splay(x);
while (ch[x][0]) x = ch[x][0];
Splay(x); return x;
}
void MakeRoot(int x)
{
Access(x); Splay(x); rev[x] ^= 1;
}
/*====================*/
void Cut(int u, int v)
{
Split(u, v);
if (fa[u] == v && !ch[u][1])
{
ch[v][0] = fa[u] = 0;
}
}
void Link(int u, int v)
{
MakeRoot(u); fa[u] = v;
}
/*====================*/
void Split(int u, int v)
{
MakeRoot(u); Access(v); Splay(v);
}
/*====================*/
int LCA(int u, int v)
{
Access(u); int ans = 0;
for (int child = 0; v; child = v, v = fa[v])
{
Splay(v); ch[v][1] = child; ans = v;
}
return ans;
}
};
LCA
樹剖法
class C_LCA
{
private:
struct Node
{
int pre, dep, siz, son, top;
Node(void)
{
pre = -1, dep = +0, siz = +1, son = -1, top = -1;
}
};
/*====================*/
int root;
vector<int>* G;
vector<Node>node;
/*====================*/
void DFS1(int pre, int cur)
{
node[cur].pre = pre;
node[cur].dep = node[pre].dep + 1;
for (auto nxt : G[cur])
{
if (nxt != pre)
{
DFS1(cur, nxt);
node[cur].siz += node[nxt].siz;
if (node[cur].son == -1)
{
node[cur].son = nxt;
}
else if (node[nxt].siz > node[node[cur].son].siz)
{
node[cur].son = nxt;
}
}
}
}
void DFS2(int cur, int top)
{
node[cur].top = top;
if (node[cur].son != -1)
{
DFS2(node[cur].son, top);
for (auto nxt : G[cur])
{
if (nxt == node[cur].pre)continue;
if (nxt == node[cur].son)continue;
DFS2(nxt, nxt);
}
}
}
public:
void Init(int n, vector<int>G[], int root = 1)
{
this->G = G;
this->root = root;
node.assign(n + 1, Node());
DFS1(root, root); DFS2(root, root);
}
int operator()(int u, int v)
{
while (node[u].top != node[v].top)
{
int topu = node[u].top;
int topv = node[v].top;
if (node[topu].dep > node[topv].dep)
{
u = node[topu].pre;
}
else
{
v = node[topv].pre;
}
}
return node[u].dep > node[v].dep ? v : u;
}
};
ST表法
class C_LCA
{
private:
vector<int>* G;
vector<int>dfn;
vector<vector<int>>table;
/*====================*/
void DFS(int pre, int cur)
{
table[0][dfn[cur] = ++dfn[0]] = pre;
for (auto nxt : G[cur])if (nxt != pre)DFS(cur, nxt);
}
int Get(int x, int y)
{
return dfn[x] < dfn[y] ? x : y;
}
public:
void Init(int n, vector<int>G[], int root = 1)
{
this->G = G; dfn.assign(n + 1, 0);
table.assign(__lg(n) + 1, vector<int>(n + 1, 0));
/*====================*/
DFS(0, root);
for (int d = 1; (1 << d) <= n; ++d)
{
for (int i = 1; i + (1 << d) - 1 <= n; ++i)
{
table[d][i] = Get(table[d - 1][i], table[d - 1][i + (1 << (d - 1))]);
}
}
}
int operator()(int u, int v)
{
if (u == v)return u;
if ((u = dfn[u]) > (v = dfn[v]))swap(u, v);
int d = __lg(v - u++); return Get(table[d][u], table[d][v - (1 << d) + 1]);
}
};
樹雜湊
class C_TreeHash
{
private:
vector<int>* G;
map<vector<int>, int>mp;
/*====================*/
int DFS(int pre, int cur)
{
vector<int>vec;
for (auto nxt : G[cur])
{
if (nxt != pre)
{
vec.push_back(DFS(cur, nxt));
}
}
sort(vec.begin(), vec.end());
if (mp.find(vec) == mp.end())
{
mp[vec] = mp.size();
}
return mp[vec];
}
public:
int operator()(vector<int>G[], int root)
{
this->G = G;
return DFS(root, root);
}
};
樹分治
點分治
class C_TCD
{
private:
vector<int>* G;
/*====================*/
vector<int>siz;
vector<bool>rooted;
/*====================*/
int centroid, ans_part;
/*====================*/
void DFS(int pre, int cur, int all)
{
siz[cur] = 1; int max_part = 0;
for (auto nxt : G[cur])
{
if (nxt == pre)continue;
if (rooted[nxt])continue;
DFS(cur, nxt, all);
siz[cur] += siz[nxt];
max_part = max(max_part, siz[nxt]);
}
max_part = max(max_part, all - siz[cur]);
if (max_part < ans_part)
{
ans_part = max_part, centroid = cur;
}
}
int Centroid(int cur, int all)
{
centroid = -1; ans_part = all;
DFS(cur, cur, all); return centroid;
}
/*====================*/
void CalcSon(int pre, int cur)
{
/*
新增cur到右樹tree中
*/
for (auto nxt : G[cur])
{
if (nxt == pre)continue;
if (rooted[nxt])continue;
CalcSon(cur, nxt);
}
}
void CalcRoot(int root)
{
/*
初始化左庫lib
*/
for (auto son : G[root])
{
if (!rooted[son])
{
/*
初始化右樹tree
*/
CalcSon(son, son);
/*
遍歷右樹tree匹配左庫lib
*/
/*
新增右樹tree到左庫lib
*/
}
}
}
/*====================*/
void DividTree(int root)
{
rooted[root] = true; CalcRoot(root);
for (auto son : G[root])
{
if (!rooted[son])
{
DividTree(Centroid(son, siz[son]));
}
}
}
public:
void operator()(int n, vector<int>G[])
{
this->G = G;
siz.assign(n + 1, 0);
rooted.assign(n + 1, false);
DividTree(Centroid(1, n));
}
};
K級祖先
class C_KthAncestor
{
private:
int n, root; vector<int>* G;
/*====================*/
struct Node
{
int pre, dep, siz, son;
int top, dfn, idx;
Node(void)
{
pre = -1; dep = +0;
siz = +1; son = -1;
dfn = +0, idx = +0;
top = -1;
}
};
/*====================*/
vector<Node>node; int cnt;
/*====================*/
void DFS1(int pre, int cur)
{
node[cur].pre = pre;
node[cur].dep = node[pre].dep + 1;
for (auto nxt : G[cur])
{
if (nxt != pre)
{
DFS1(cur, nxt); node[cur].siz += node[nxt].siz;
if (node[cur].son == -1)
{
node[cur].son = nxt;
}
else if (node[nxt].siz > node[node[cur].son].siz)
{
node[cur].son = nxt;
}
}
}
}
void DFS2(int cur, int top)
{
node[cur].top = top;
node[cur].dfn = ++cnt;
node[cnt].idx = cur;
if (node[cur].son != -1)
{
DFS2(node[cur].son, top);
for (auto nxt : G[cur])
{
if (nxt == node[cur].pre)continue;
if (nxt == node[cur].son)continue;
DFS2(nxt, nxt);
}
}
}
public:
void Init(int n, vector<int>G[], int root = 1)
{
node.assign(n + 1, Node());
this->n = n, this->root = root, this->G = G;
cnt = 0; DFS1(root, root); DFS2(root, root);
}
int operator()(int x, int k)
{
int topx = node[x].top;
while (k > 0)
{
topx = node[x].top;
if (node[x].dep - node[topx].dep < k)
{
k -= node[x].dep - node[topx].dep + 1;
x = node[topx].pre;
}
else
{
x = node[node[x].dfn - k].idx; k = 0;
}
}
return x;
}
};
樹鏈剖分
重鏈剖分
class C_HLD
{
public:
int n, root; vector<int>* G;
/*====================*/
struct Node
{
int pre, dep, siz, son;
int top, dfn, idx;
Node(void)
{
pre = -1; dep = +0;
siz = +1; son = -1;
dfn = +0, idx = +0;
top = -1;
}
};
private:
vector<Node>node; int cnt;
/*====================*/
void DFS1(int pre, int cur)
{
node[cur].pre = pre;
node[cur].dep = node[pre].dep + 1;
for (auto nxt : G[cur])
{
if (nxt != pre)
{
DFS1(cur, nxt); node[cur].siz += node[nxt].siz;
if (node[cur].son == -1)
{
node[cur].son = nxt;
}
else if (node[nxt].siz > node[node[cur].son].siz)
{
node[cur].son = nxt;
}
}
}
}
void DFS2(int cur, int top)
{
node[cur].top = top;
node[cur].dfn = ++cnt;
node[cnt].idx = cur;
if (node[cur].son != -1)
{
DFS2(node[cur].son, top);
for (auto nxt : G[cur])
{
if (nxt == node[cur].pre)continue;
if (nxt == node[cur].son)continue;
DFS2(nxt, nxt);
}
}
}
public:
void Init(int n, vector<int>G[], int root = 1)
{
node.assign(n + 1, Node());
this->n = n, this->root = root, this->G = G;
cnt = 0; DFS1(root, root); DFS2(root, root);
}
Node& operator[](int idx) { return node[idx]; }
};
樹的重心
class C_TreeCentroid
{
private:
vector<int>siz;
vector<int>* G;
int centroid, ans_part;
/*====================*/
void DFS(int pre, int cur, int all)
{
siz[cur] = 1; int max_part = 0;
for (auto nxt : G[cur])
{
if (nxt != pre)
{
DFS(cur, nxt, all);
siz[cur] += siz[nxt];
max_part = max(max_part, siz[nxt]);
}
}
max_part = max(max_part, all - siz[cur]);
if (max_part < ans_part)
{
ans_part = max_part, centroid = cur;
}
}
public:
int operator()(int n, vector<int>G[])
{
this->G = G; siz.assign(n + 1, 0);
centroid = -1; ans_part = n;
DFS(1, 1, n); return centroid;
}
};
樹上路徑求交
假設當前要求路徑 \((a,b)\) 和 \((c,d)\) 的交。
設 \(d_x\) 表示 \(x\) 的深度。
先求出 \(p[4]={lca(a,c),lca(a,d),lca(b,c),lca(b,d)}\)。
將 \(p\) 陣列按深度排序,取出深度較大的兩個,記為 \(p0,p1\)。
若存在交,則 \((p0,p1)\) 即所求。
現在只需要判斷路徑是否有交。
若 \(p0\neq p1\),則一定有交。
否則若 \(d_{p0}=\max(d_{lca(a,b)},d_{lca(c,d)})\),也有交。
否則路徑不相交。
樹上啟發式合併
對於以 u 為根的子樹
①. 先統計它輕子樹(輕兒子為根的子樹)的答案,統計完後刪除資訊
②. 再統計它重子樹(重兒子為根的子樹)的答案,統計完後保留資訊
③. 然後再將重子樹的資訊合併到 u上
④. 再去遍歷 u 的輕子樹,然後把輕子樹的資訊合併到 u 上
⑤. 判斷 u 的資訊是否需要傳遞給它的父節點(u 是否是它父節點的重兒子)
void DFS(int root, int cur)
{
cnt[node[cur].val]++;
if (cnt[node[cur].val] > maxcnt)
{
ans[root] = node[cur].val;
maxcnt = cnt[node[cur].val];
}
else if (cnt[node[cur].val] == maxcnt)
{
ans[root] += node[cur].val;
}
for (auto nxt : G[cur])
{
if (nxt == node[cur].pre)continue;
if (nxt == node[root].son)continue;
DFS(root, nxt);
}
}
void DSU(int cur, bool keep)
{
for (auto nxt : G[cur])
{
if (nxt == node[cur].pre)continue;
if (nxt == node[cur].son)continue;
DSU(nxt, false);
}
if (node[cur].son != -1)DSU(node[cur].son, true);
if (node[cur].son != -1)
{
ans[cur] = ans[node[cur].son];
}
DFS(cur, cur);
if (keep == false)
{
maxcnt = 0;
for (int i = node[cur].dfn; i < node[cur].dfn + node[cur].siz; ++i)
{
cnt[node[node[i].idx].val]--;
}
}
}