长链剖分
长链剖分
定义
记录 \(dep[x]\) 表示 \(x\) 到它子树中的叶子的最长路径长,其中叶子节点 \(dep[x]=0\)
那 \(x\) 到这个叶子的路径就是一条长链,对应的子节点为 \(mson[x]\),\(x\) 到剩下子节点的边为轻边
本质上,是和重链剖分划分方式不同的另一种树剖形式
性质
-
每个子节点属于有且仅有一条长链
-
长链互不相交
-
树上所有长链的长度和为 \(O(n)\)
-
节点向上跳长链,所在长链的长度递增
-
节点 \(x\) 在树上的 \(k\) 级祖先 \(y\) 所在长链长度 \(\ge k\)
-
节点沿长链跳到根节点,最多经过 \(O(\sqrt n)\) 条长链
代码:
剖分
inline void dfs1(int x)
{
for(reg int y : edge[x])
{
dfs1(y);
if(dep[y] + 1 > dep[x]) mson[x] = y, dep[x] = dep[y] + 1;
}
}
inline void dfs2(int x)
{
dfn[x] = ++cnt;
if(mson[x]) dfs2(mson[x]);
for(reg ll y : edge[x])
if(y != mson[x]) dfs2(y);
}
应用:
优化 DP
P5904 [POI2014] HOT-Hotels 加强版
刻画选的点的形态,是 \(i,j\) 到它们的 \(lca\) 距离相等,为 \(d\),\(lca\) 上又挂了一条长为 \(d\) 的链
分在子树外和在子树内,但不与 \(i,j\) 在相同子树两种情况
在子树外时我们要在上面那条链「拐弯」的那个点统计,保证不重不漏
发现在子树内的情况也可以被统一,\(lca\) 与那个点重合
所以设 \(g(x,i)\) 表示 \(x\) 子树内两个距 \(lca\) 为 \(d\) 的点,\(lca\) 离 \(x\) 距离为 \(d-i\) 的方案数,也就是说,\(x\) 另一棵子树内还要找长为 \(i\) 的链即可贡献给答案
那就设 \(f(x,i)\) 表示 \(x\) 子树内与 \(x\) 距离为 \(i\) 的点的个数
发现第二维只跟深度有关,长链剖分优化
\(f(x)\) 正常优化,但是 \(g(x)\) 处要支持在前面插入一个数,因为用到的是 \(g(y,i+1)\)
预留空间,为了在前面插入,先预留长链长度,再放置初始位置,再为了在后面插入,预留长链长度,总共需要 \(8\) 倍空间?
void dfs(int x, int fath)
{
fa[x] = fath;
for(int y : edge[x])
if(y != fath)
{
dfs(y, x);
if(dep[y] + 1 > dep[x]) dep[x] = dep[y] + 1, mson[x] = y;
}
}
void dp(int x)
{
dfn[x] = ++cnt, rev[mson[x]] = rev[x] - 1;
if(mson[x]) dp(mson[x]);
ans += g[rev[x]], f[dfn[x]] = 1;
for(int y : edge[x])
{
if(y == mson[x] || y == fa[x]) continue;
// 关于空间, 每条链前面有一倍空间支持后退指针, 后面有一倍空间放链头代表的链
idx += dep[y] + 5, rev[y] = idx, idx += dep[y] + 5, dp(y);
for(ll i = 1; i <= dep[y] + 1; ++i) ans += g[rev[x] + i] * f[dfn[y] + i - 1];
for(ll i = 0; i <= dep[y]; ++i)
{
if(i < dep[y]) g[rev[x] + i] += g[rev[y] + i + 1];
if(i) ans += f[dfn[x] + i - 1] * g[rev[y] + i];
}
for(ll i = 1; i <= dep[y] + 1; ++i)
{
g[rev[x] + i] += f[dfn[x] + i] * f[dfn[y] + i - 1];
f[dfn[x] + i] += f[dfn[y] + i - 1];
}
}
}
int main()
{
read(n);
for(int i = 1; i < n; ++i)
{
read(u, v);
edge[u].pb(v), edge[v].pb(u);
}
dfs(1, 0);
rev[1] = idx = dep[1] + 5, idx += dep[1] + 5, dp(1);
printf("%lld", ans);
return 0;
}
贪心
这题就是那个原题