P4219 [BJOI2014]大融合

P4219 [BJOI2014]大融合

题目大意

  • 给定 nn 点, mm 个操作如下
    • 加入一条边,保证连边的两点原来不连通
    • 询问有多少条路径通过某条边
  • 1n,m1051 \leq n,m \leq 10^5

分析

网上大多题解都是使用 LCT 来维护, 然而蒟蒻并不会 LCT, 所以这里提供一种仅使用树链剖分的解法.

不难发现某条边路径数为 sizx×(sizrootsizx)siz_x \times (siz_{root} - siz_x), 其中 xx 为边所连的点中深度较大的一个, rootroot 为树根, sizsiz 为子树大小.

发现最后 nn 个点的形态为一个森林, 所以我们可以先离线下来, 建好森林再倒序删边同时回答询问.

假设删去的边连接 x,yx, y, 且 x,yx, y 原来的深度 depx<depydep_x < dep_y. 删边会影响 xx 到根路径上的点的子树大小, 以及以 topatop_a, 其中 aayy 所在重链中 yy 一下的节点.

我们可以用树剖来维护 sizsizdepdep, 断开边时, 将 xx 到根的路径上的点的 sizsiz 都减去 sizysiz_y, 将 xx 一下的重链上节点的 toptop 修改为 yy. 查询时, sizy×(sizrootsizy)siz_y \times (siz_{root} - siz_y) 即为答案.

本做法为真·用树剖来维护树剖, 复杂度为 O(mlog2n)O(m\log^2n), 还是很快的.

#代码

只放出修改和查询时的核心代码, 细节较多, 注意更新 depdepsizsiz, 完整代码可以看我的提交

修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fa[y] = 0;
top[y] = query(1, 1, n, dfn[y]).top;
top[x] = query(1, 1, n, dfn[x]).top;
// 更新 top
if(top[y] != y){
modify(1, 1, n, dfn[y], dfn[y] + dep[tail[top[y]]] - dep[y], y); // 修改 y 所在重链 top
top[y] = y;
tail[top[y]] = tail[top[x]];
tail[top[x]] = x;
}
siz[y] = query(1, 1, n, dfn[y]).siz; // 更新 siz
while(x){
top[x] = query(1, 1, n, dfn[x]).top;
mul(1, 1, n, dfn[top[x]], dfn[x], -siz[y]); // 修改 x 到根的 siz
x = fa[top[x]];
}

查询

1
2
3
4
5
6
7
8
9
10
11
siz[y] = query(1, 1, n, dfn[y]).siz;
top[x] = query(1, 1, n, dfn[x]).top;
// 更新 siz 和 top
while(fa[top[x]]){
x = fa[top[x]];
top[x] = query(1, 1, n, dfn[x]).top;;
}
x = top[x];
// x 跳到根
siz[x] = query(1, 1, n, dfn[x]).siz;
ans[i] = 1LL * siz[y] * (siz[x] - siz[y]); //注意 long long

P4219 [BJOI2014]大融合
http://yoursite.com/2022/06/20/P4219-BJOI2014-大融合/
作者
99_wood
发布于
2022年6月20日
许可协议