POJ 3417
题意
给定1棵点数为\(n\)的树和\(m\)条附加边。
询问砍掉1条树边和1条附加边,使得该图不连通的方案数。
\(1 \leq n \leq 10^5 , 0 \leq m \leq 10^5\)
解法1
考虑枚举砍掉的树边,若不考虑附加边原图变成2个联通块,则问题转化为询问连接这2个联通块的附加边边数。若有0条则有\(m\)种方案,若有1条则有1种方案,若多于1条则有0种方案。
这2个联通块中,至少有1个是原树的子树。考虑求出DFS序\([x,y]\),则问题转化为区间问题。具体地,将附加边视为二元组\((a,b)\),则问题转化为求解\((a < x且b \in[x,y]) ||(a \in[x,y]且b > y)\)的二元组数目。
二维偏序问题,考虑离线处理。将二元组和区间分别排序。第1次从前往后扫,使得已有的二元组均满足\(b <= y\);累加\(a < x\)的二元组数目,减去\(b < x\)的二元组数目;一维偏序仅用树状数组可以简单维护。类似的,从后往前再扫1遍即可得解。时间复杂度\(O((n + m)logn)\)。代码见此
解法2
考虑1条附加边\((x,y)\)的影响:令树上\(x \rightarrow y\)的点形成1个环。这意味着,如果断掉\(x \rightarrow y\)上的任1条树边,则一定还需要断掉这条附加边才能令原图不连通。
由此,我们可以将附加边\((x,y)\)的作用视为将\(x \rightarrow y\)的树边”覆盖“了1次。若1条树边被覆盖0次,则断它之后任意选择附加边断;若被覆盖1次,则断掉覆盖它的那条附加边;若被覆盖1次以上,则没有合法方案。
将被覆盖次数视为边权,问题转化为如何快速为树上一条路径边权增加一。由于仅最后需要查询,不妨使用树上差分的技巧。即两点间路径边权/点权统一增值时,仅在两点的差分数组上增值,在两点的\(lca\)的差分数组上减回。差分数组的子树和即为实际边权/点权大小。
复杂度取决于\(lca\)的实现方式,使用\(Tarjan\)算法可以接近\(O(n + m)\);倍增算法则为\(O((n + m)logn)\)。
\(Tarjan\)算法:[代码见此](https://github.com/littlewyy/OI/blob/master/poj 3417.cpp)
\(lca\)的Tarjan算法
基于\(dfs\)的离线算法。将已访问但尚未回溯的点标记为1,将已访问且已回溯的点标记为2;则访问\(x\)时,\(x\)的所有祖先的标记都为\(1\);若在\(x\)上的询问中,另1顶点\(y\)的标记为2,则询问\((x,y)\)的答案即为从\(y\)向上跳父亲到达的第1个标记为\(1\)的点;通过并查集的路径压缩保证跳父亲的时间复杂度。