UOJ23口胡

qpOrzgyh20

考虑一个 DP:设有 \(dp[u][k]\) 条路径满足其中一个端点为 \(u\),另一个端点在 \(u\) 的子树内。

先把圆方树建出来,在上面转移会方便一些。

对于方点不予考虑,直接考虑圆点。设对于一个圆点 \(u\) 的儿子集合为其儿子(方点)在圆方树上的儿子集合。(\(S_u\))那么转移有:

\[dp[u][k]=\sum_{i=1}^{|S_u|}dp[v_i][k-i]+dp[v_i][k-(|S_u|+1-i)] \]

如果将 \(f[u][k]\) 考虑成生成函数 \(F_u(x)\),那么相当于:

\[F_u(x)=\sum_{i=1}^{|S_u|}F_{v_i}(x)(x^i+x^{|S_i|+1-i}) \]

我们只需要求出 \(F_1(x)\) 即可。

众所周知点分治可以用来优化 DP,我们也理所当然可以用点分治来优化这个转移。

容易知道得出一个连通块最浅的节点的 \(F(x)\) 后,下面的东西就没用了。所以一个 \(Solve(u)\) 应该是求解的是 当前连通块中最浅节点的 \(F(x)\)

那么点分治将会把这个连通块分成若干个连通块,一个在最顶上的连通块和一车在下面的连通块。

我们可以先对最顶上的连通块调用 Solve 进行求解,然后处理下面一车连通块对最顶上的连通块调用 Solve,只需要考虑下面的连通块对最顶上的节点的贡献即可。

容易发现,仙人掌上的两个节点之间,影响路径条数的一定是环。而在这个生成函数模型中,环相当于一个有两项的多项式。

显而易见的是,我们只需要想个办法把这些多项式拉出来做分治乘法就可以 \(O(n\log^3n)\) 了。

有点儿慢,但是看上去有救。

一个算法的复杂度与其信息量有关,信息量不是很大那么就还有救回来的机会。

容易知道在对顶上的连通块点分治的时候一定会算其到最浅的节点的多项式的积,这些多项式正好也是我们需要用到的。

并且这些多项式肯定会将这条路径分成 \(O(\log n)\) 段,从上到下的长度,每往下跳一段长度就会减半。所以这部分复杂度可以优化到 \(T(n)=T(n/2)+O(n\log n)=O(n\log n)\)

当然,你也可以用倍增预处理这些多项式的乘积,然后离线搞搞搞,时空应该都是对的(

这样以来就可以 \(O(n\log^2n)\) 做掉了。

在转移的时候需要处理好细节,不然可能会炸空间和时间。。。

posted @ 2022-04-06 16:20  Prean  阅读(24)  评论(1编辑  收藏  举报
var canShowAdsense=function(){return !!0};