传送门数组
官方题解其实讲的挺清楚了,就是锅有点多……优化
一个经典(反正我是不会)的容斥:最后的答案=对于每一个点可以以它做为集合点的方案数-对于每条边可以以其两个端点做为集合点的方案数。缘由是:对于每一种合法方案,集合点必定是树上的一个连通块,知足$n=m+1$。算点时,这种方案被算了$n$次;算边时,这种方案被算了$m=n-1$次,因此每个方案都刚好被算了一次。spa
有$DP$:设$f_i-1$表示选择了包含$i$和$i$的子树中的点的一个连通块的方案数,转移枚举每个儿子选不选。而后设$g_i$表示以$i$为原树的根,选择了包含$i$和$i$的子树中的点的一个连通块的方案数,这个在求完$f$以后换根DP,换根的时候能够同时计算出每条边的方案。调试
改一下$N=L$的DP:设$f_{i,j}-1$表示选择了包含$i$和$i$的子树中的点的一个连通块,其中距离$i$点最远的点与$i$距离$\leq j$的方案数,$g_{i,j}$表示以$i$为原树的根,选择了包含$i$和$i$的子树中的点的一个连通块,其中距离$i$点最远的点与$i$距离为$\leq j$的方案数。blog
一样能够求完$f$后换根DP求$g$,可是有个问题:如何从$f_{fa_x}$中去掉$f_x$的贡献。能够在计算$f$的时候计算每个点合并全部孩子的一个前缀的答案、合并全部孩子的一个后缀的答案,这样把一个前缀和一个后缀拼起来就能够获得去掉$f_x$的贡献的$f_{fa_x}$。递归
##链get
枚举一下点和边,方案数能够直接算同步
52pts代码io
能够枚举选择的连通块的根,用长链剖分+线段树优化,与正解关系不大因此略过打包
从$NL \leq 10^7$开始。稍微修改一下$g_{i,j}$的定义:设$g_{i,j}$表示选择$i$、不选择$i$的子树,选择的连通块中最大距离$\leq j$的方案数。
注意到$f$能够长链剖分优化。在长链剖分的转移中咱们须要支持后缀乘(由于后缀有一段要乘的值相同)、总体加(转移完成以后全部$f_{i,j}$须要$+1$),能够给长链剖分打乘法标记$a$和加法标记$b$解决,一次后缀乘就把前面没有乘的部分乘上逆元。可能会有乘$0$的问题,因此还须要一个后缀赋值标记,当乘$0$时把当前的后缀赋值为$-\frac{b}{a}$。
$g$是一个换根DP,可是也能够用长链剖分优化。将重链和轻边的转移分开考虑:①对于重链直接暴力将轻儿子的$f$值转移过来,复杂度跟长链剖分同样;②对于轻边,假设这条轻边转移到的点$x$的子树深度为$p$,那么由于在最后统计答案的时候只须要每一个点的$g_{x,L}$,因此$x$子树中只有$g_{x,j}(j \in [L - p , L])$有用,能够只转移这一些值。那么每一条轻边就只会转移轻子树深度个$g$,总复杂度也是均摊$O(n)$的
咱们还须要解决在转移$g$的过程当中要取孩子的一个前缀和一个后缀的问题。能够主席树可是$O(nlogn)$有点慢。考虑按照求$f$时DFS顺序的反序DFS求$g$,咱们到达一个点、即将递归到下一个点的时候,将这个点对于当前点的$f$的贡献消除,这样能够获得前缀的值,再拿另外一个数组维护一下后缀的$f$值就能够获得前缀和后缀。支持撤销只须要在每一次从轻边修改一个点时把全部标记和将要修改的位置的值记录下来打包丢到一个栈里面。
有一个预处理逆元的科技:可知要求逆元的数必定是$f_{x , p}$,其中$p$是$x$的子树大小,因此用$L=N$部分分的方法将全部$dp_{x,p}$预处理出来,而后用相似于预处理阶乘逆元的方式预处理逆元,在更新乘法标记$a$的同时同步更新$a^{-1}$,就能够作到严格的$O(n)$求解。
细节很是很是的多……写题两小时调试一成天