题意:一棵$n$个点的树,每个点$i$有权值$w_i$,$m$条路径,对每个点i求出有多少条路径经过i且路径起点与$i$的距离为$w_i$。
最近清noip题目发现这道题还没有做。回头来看,这道题仍然给我不小的收获。
我们将路径$(u, v)$拆成向上路径$(u, lca)$和向下路径$(lca, v)$。首先考虑向上的路径。
一条向上的路径$(u, v)$对点i有贡献,当且仅当:
1、u在i的子树中
2、$dep[u] = dep[i] + w[i]$
3、路径经过i,即不会在到达i之前结束。
由于2条件中的$dep[i] + w[i]$只与i有关,$dep[u]$只与u有关,我们便有了如下做法:
存储下以每个节点i作为起点的路径$(u, v)$的$dep[u]$值(这里就是$dep[i]$),以每个节点i作为终点的路径$(u, v)$的$dep[u]$值。维护一个全局的桶,每当遍历到一个点i时:
1、处理i的子树
2、将以i为起点路径的$dep[u]$值加入桶。
3、统计桶里值=$dep[i] + w[i]$的元素个数,更新$ans[i]$。
4、将以i为终点路径的$dep[u]$值从桶里删去。
这里本质上是将桶中+1/-1事件挂在每个点上,每到一个点时查询桶中对应位置的子树和。这个打正负标记求和的思想在线性结构和树结构上都有广泛运用,常常与差分一起出现。
这里呢?我们发现我们通过这种办法求出来的元素可能并不全在i子树中,也包括了从其他子树出发的向上路径。我们可以在dfs进入i点时,记录一下当前的答案,处理完i子树之后,将现在的答案与刚刚进入i时的答案相减,就得到了真正的答案了。这就是差分思想的一次体现。
对于向下的路径,我们发现条件变化了。重新考虑原路径。对于原路径$(u, v)$分出的向下路径$(lca, v)$,对i有贡献的条件是:$dep[u] - 2 \times dep[lca] = w[i] - dep[i]$。于是我们也可以仿照上面的做法,先把向下路径反过来变为向上路径,将左边式子的值放入桶里,用右边式子的值查询。但是这个桶可能会有负数下标,要注意一下。
最后,我们发现对于一条完整的路径,lca可能会被算两次贡献。这在读入路径的时候就判断减掉即可。
做了一些noip的题目,发现近两年的最难题目都出在树上,而且都与正负标记求和与树上差分联系起来。这一点要着实注意啊。