长链剖分

长链剖分

定义

记录 \(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

Dominant Indices

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\) 的点的个数

\[ans \gets g(x,0)+\sum_{i=1}g(x,i)\times f(y,i-1)+f(x,i)\times g(y,i+1) \\ g(x,i)\gets g(y,i+1)+f(x,i)\times f(y,i-1) \\ f(x,i)\gets f(y,i-1) \]

发现第二维只跟深度有关,长链剖分优化

\(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;
}

贪心

CF526G Spiders Evil Plan

20231004 模拟赛订正

这题就是那个原题

posted @ 2023-02-18 17:06  KellyWLJ  阅读(9)  评论(0编辑  收藏  举报  来源