luogu P5291 [十二省联考2019]希望

luogu

loj

无论最终结果将人类历史导向何处
(quad)我们选择
(quadquad)(large{希望})

诶我跟你讲,这题超修咸的

下面称离连通块内每个点距离不超过(L)的点为中心点.首先可以注意到,所有连通块的共同的中心点一定是个连通块,所以可以写一个暴力状压,表示中心点状态为(S)的方案数,然后随便枚举一个连通块转移即可

暴力代码

中心点是连通块很烦,考虑转化一下.其实答案为只考虑中心点为一个点的方案减去只考虑中心点为一条边上的两个点的方案,因为考虑任意一个连通块,在算中心点为一个点会算点数(n)次,在算中心点为一条边会算边数(m)次,然后又有连通块一定满足(n-m=1),所以这样算每个连通块都会被算一次

先考虑中心点为一个点,设(f_{x,l})表示在(x)子树内,包含(x),且到(x)最远距离不超过(l)的连通块个数,转移就是(f_{x,l}=prod_{yin son(x)} (f_{y,l-1}+1)),就是每个点儿子可选或不选,然后还要设(g_{x,l})表示不包含(x)子树其他点,包含(x),且到(x)最远距离不超过(l)的连通块个数转移是(g_{x,l}=g_{fa,l-1}prod_{yin son(fa),y eq x} (f_{y,l-2}+1)).那么这个点的贡献就是((f_{x,l}g_{x,l})^k).一条边也类似,只不过注意两个到中心点的距离都要(le L),所以是((f_{x,l-1}(g_{x,l}-1))^k)

这个暴力是(O(nL))的,不过因为下标和深度有关,所以可以长链剖分优化.(f)的话,一个点先继承重链状态,然后轻儿子暴力乘上去.注意第二维大于轻儿子第二维最大值(也就是(y)往下最多能延伸多深,假设是(dep_y))的部分,是要乘上(f_{y,dep_y})的,所以还要支持后缀乘,用个可持久化线段树维护即可(因为要算g要还原每个(x)的dp值)

然后就是(g),首先重链,重儿子直接继承当前节点状态.对于轻儿子,看上去要转移的状态比较多,不过可以注意到一个东西,就是一个点(x)有用的(g)的第二维的范围是([L-dep_x,L]),那么只要转移这些范围就好了,复杂度是(sum)轻儿子的(dep),所以复杂度也是对的.注意转移到儿子时要扣掉自己本身的贡献,一个想法是用所有儿子(f)的乘积乘上自己(f)的逆元,但是自己的(f)是有可能为0的,所以转化成前缀积乘后缀积的形式,后缀积可以在做的时候维护

然后那个可持久化线段树有个log,很不优秀.然后我们又发现前缀积其实在做的时候已经求过了,所以我们只要开个栈栈序撤销合并的操作就得到前缀积.然后那个后缀乘也可以看做是全部乘一个值然后前缀乘上逆元,所以考虑维护dp数组的乘法标记(a)和加法标记(b),我们在dp数组上存值(x),然后真实值应该是(ax+b),每次后缀乘(c),直接给(a)(b)乘上(c),然后前面的值暴力改成((ax+b-bc)(ac)^{-1}).还有就是可能(c=0),那么在维护赋值标记(pl,nm),表示下标为(pl)之后的值都是(nm),每次把(nm)改成(-b*a^{-1})即可

不过逆元的问题还是没解决.我们发现要求逆元的值一定是(f_{x,dep_x}),那么可以参考阶乘求逆元,先把前缀积搞出来,然后求出最后一项的逆元,然后用前缀积递推出就可以算出每个数的逆元

差不多了,然后剩下的就看你的了(逃

代码

duliu

原文地址:https://www.cnblogs.com/smyjr/p/10725717.html