2024.3.14 树链剖分 and 树上启发式合并
树链剖分
//参考 https://oi-wiki.org/graph/hld/#%E4%BE%8B%E9%A2%98
//例题 //https://loj.ac/p/10138 https://www.luogu.com.cn/problem/P3379
vector<int>G[N];
int son[N];
int siz[N];
int fa[N];
int dep[N];
void dfs1(int o) {
son[o] = -1;
siz[u] = 1;
for (int v:G[u]){
if(dep[v]) continue;
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
siz[u]+=siz[v];
if(!son[u]||siz[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u, int f) {
top[u] = f;
dfn[u] = ++cnt;
rnk[cnt] = u;
if (!son[u]) return;
dfs2(son[u], f);
for (int v : G[u]) {
if(v==son[u]||v==f) continue;
dfs2(v,v);
}
}
// 因为重链是连续的dfn序列,所以根据这个特性,将树变为一个序列配合线段树,可以查询点、边的(最大值,和)
// 原理:位于轻链,暴力慢慢移动到重链;位于重链,线段树优化移动到重链头,直到两者位于同一条重链,再用线段树线段树计算距离
// st 是线段树结构体
int querymax(int x, int y) {
int ret = -inf, fx = top[x], fy = top[y];
while (fx != fy) {
if (dep[fx] >= dep[fy])
ret = max(ret, st.query1(1, 1, n, dfn[fx], dfn[x])), x = fa[fx];
else
ret = max(ret, st.query1(1, 1, n, dfn[fy], dfn[y])), y = fa[fy];
fx = top[x];
fy = top[y];
}
if (dfn[x] < dfn[y])
ret = max(ret, st.query1(1, 1, n, dfn[x], dfn[y]));
else
ret = max(ret, st.query1(1, 1, n, dfn[y], dfn[x]));
return ret;
}
树上启发式合并:
//https://codeforces.com/problemset/problem/600/E
void update(int u, int v) {
cnt[a[u]] += v;
if(cnt[a[u]] > maxv) maxv = cnt[a[u]], sum = a[u];
else if(cnt[a[u]] == maxv) sum += a[u];
}
int L[N],R[N];
int dfn[N],tot;
int siz[N];
void dfs1(int u, int fa)
{
siz[u] = 1;
L[u] = ++tot;
dfn[ts] = u;
for (int v:G[u]){
if(v == fa) continue;
dfs1(v,u);
size[u]+=siz[v];
if(siz[v]>sz[son[u]]) son[u]=j;
}
R[u]=tot;
}
void dfs2(int u, int fa, int del) {
for (int v:G[u]){
if(v==son[u]||v==fa) continue;
dfs2(v,u,1);
}
if(son[u]) dfs2(son[u], u, 0);
for (int v:G[u]){
if(v==son[u]||v==fa) continue;
for (int i=L[v];i<=R[v];i++){
update(dfn[i], 1); // 子树合并
}
dfs2(v,u,1);
}
update(u, 1);
ans[u] = sum;
if(del) {
for(int i=L[u];i<=R[u];i++)
update(dfn[i], -1); // 子树合并
maxv = sum = 0;
}
}
过知识点