NOI2020 命运

\(\text{dp}(u,j)\) 表示 \(u\) 子树内,最长的还需要被满足的链的链顶的深度 \(= j\) 的方案数。

这里我们认为如果 \(j\ge \text{dep}_u\),表示这个方案再也不可能被满足了,要有 \(\text{dp}(u,j)=0\)

否则考虑 \(u\) 的每个儿子 \(v\)\(u\) 的贡献,考虑以 \(v\) 这个点为最底下那个点的最长的一条链,他的深度为 \(D\),讨论一下:

  • 不覆盖 \((u,v)\)\(\text{dp}(u,j)\times \text{dp}(v,k)\to \text{dp}(u,\max(j,k,D))\)
  • 覆盖 \((u,v)\)\(\text{dp}(u,j)\times \sum \text{dp}(v,k)\to \text{dp}(u,j)\)

考虑线段树合并,第二种合并很简单,考虑第一种合并,设新的是 \(\text{dp}'(u,j)\),发现有

\[\text{dp}'(u,j)=\begin{cases}0&,j<D\\\sum_{\max(x,y)=j}\text{dp}(v,x)\text{dp}(u,y)&,j\ge D\end{cases} \]

考虑直接维护前缀和,重新设 \(\text{dp}(u,j)\) 表示最深的链顶深度 \(\le j\) 的方案数。

那么转移可以简练地写成

for(int j=0;j<dep[u];j++)
	dp[u][j]=1ll*(dp[v][dep[v]-1]+(j<M[v]?0:dp[v][j]))*dp[u][j]%mod;
//M[v] 是以 v 为最底端的最短链的链顶深度

写到这份上了,线段树合并维护就很简单了吧!

这是带标记的线段树合并。很自然的想法是,在线段树合并递归下去之前,我们先把 \(p,q\) 的标记下传。但由于我们肯定是动态开点,下传标记的时候,如果没有儿子,需要新建儿子然后把标记传下去。

但当线段树合并的时候,如果遇到两个节点 \(p,q\) 都没有儿子,但都有标记,那么会直接把 \(p,q\) 的子树都建出来。此时的解决办法是,我们特判 \(p,q\) 中任一节点没有儿子的情形(不妨设其为 \(q\)),这种情况下 \(q\) 那边的所有叶节权值均为 \(f_q(0)\),其中 \(f_q\in F\) 表示 \(q\) 节点上的标记函数。

如果仅特判两边都没有儿子的情形,复杂度仍然是错误的。

那么我们可以直接在 \(p\) 节点上打标记,表示这个区间内的所有元素 \(x\) 均由 \(x\leftarrow x\times f_q(0)\),其中 $\times $ 是叶节点的操作。由于多了一种操作,还需要改改标记函数。那么最终我们要维护的就是区间覆盖,区间加,区间乘。这个还是比较简单的。

综上本题在 \(O(n\log n+m)\) 时间内解决。

posted @ 2023-06-28 17:11  云浅知处  阅读(29)  评论(0编辑  收藏  举报