G
树状数组 + 树上启发式合并
考虑计算所有点作为 \(z\) 时的贡献,最后加和。
相当于所有包含 \(z\) 的路径 \((x,y)\),其中满足:\(w_{x}=w_{y}\) 且 \(w_{z}>w_{x}\),设现在要计算点 \(u\) 作为 \(z\) 的答案:将整个树分成 以 \(u\) 为根的子树 和 之外。则贡献有两种:
- \(x\) 在子树内,\(y\) 在子树外
- \(x,y\) 均在子树内
快速计算所有子树中每种权值大小的个数,显然是树上启发式合并。现在考虑如何利用这个信息计算贡献:
情况1很简单:先统计出每种权值的总数 \(sum[w]\),则设子树内有 \(cnt[w]\)个,子树外就有 \(sum[w] - cnt[w]\) 个。计算子树 \(u\) 中情况1的贡献,即计算:
统计的是前缀信息,则先将权值离散化,再用一个树状数组实时维护 每种权值 上面的式子即可。
对于情况2,熟悉套路的话很快就能看出来贡献可以用 子树大小和的平方 \(-\) 子树大小平方的和来算。其中前者只需要知道子树内某种权值的数量;而直接计算某个子树中 每个子树分支的某种权值个数的平方再加和 是个难点。
但可以将思维转化一下:可以直接看以 \(u\) 为根的子树内权值 \(<\)
\(u\) 的父亲权值的点的个数,对所有这样的子树作统计即可。计算这个和上面的方法是相同的。
注意情况2最后算出来要除以 \(2\),因为 \((u,v)\) 和 \((v,u)\) 重复计算了。具体细节见代码。
code
H
\(n<=3000\),因此直接考虑任意一个子区间可以在多少种分割方法中出现即可。用ST表维护区间最大值和最小值,然后用隔板法计算分割方法数。
code