P7518 宝石
2023-08-09 20:33:07 # OI # Problem

题面

P7518

思路

题意:严格按照P1,P2,P3,P4P_1,P_2,P_3,P_4\dots种宝石的顺序取,每个宝石只取一个,那么最多能取多少?

先看只有从叶子到根的情况:

image.png

假如只需要求黑点ss到紫点tt这条路径上能得到的最多的宝石。

我们最朴素的想法其实就是求一个路径上的严格单调上升子序列,但是这样复杂度是O(n)O(n)的,qq次询问就是O(nq)O(nq)的复杂度,我们是接受不了的。

那么就想想别的法子,怎样能更快、更好呢?

树上倍增倒是挺快的,那我们不妨试试。定义fi,0f_{i,0}为从编号为i的节点开始拿202^0个最近的宝石所到达的位置,假如第ii号位置的宝石种类为pkp_k,那么下一个拿的宝石就是第pk+1p_{k+1}种,我们fi,0f_{i,0}就是该种宝石且离ii最近的那颗。

如果我们能把所有的fi,0f_{i,0}给预处理出来,那么就可以倍增求解fs,log(st)f_{s,log({s\to{t})}}的值了。

那么怎么预处理这个东西呢?

首先我们来用一下树上的 dfs 序,以点xx根的子树的 dfs 序的范围是[dfsx,dfsx+sizex1][dfs_x,dfs_x+size_x-1],其中sizexsize_x代表以xx为根的子树大小,假如yy在以xx为根的子树中,当且仅当yy的 dfs 序在上面这个范围内。

而对于每一个节点,我们都可以预处理出它的 dfs 序的区间,我们把它们当作一个个nodenode,并以左端点升序排序(即 子树的根节点的 dfs 序),那么现在就得到了一个左端点单调的序列,现在要在左端点dfsnx\le{dfsn_x}的序列中找满足右端点dfsnx\ge{dfsn_x}的最大左端点,因为左端点最大即代表这个节点越深,那么到xx是最近的。

而每次在一堆nodenode中找右端点最大的那个,这个可以用 ST 表预处理出每个节点对应的sti,jst_{i,j}表示从iinodenode开始走2j2^jnodenode能走到的最大右端点,可以开动态空间来分配每种宝石的 ST 表,然后以单调的左端点进行二分,在所有左端点小于等于dfsnxdfsn_xnodenode中判断右端点是否dfsnx\ge{dfsn_x}check()check()一下它是否满足条件,如果满足那么左端点就加,直到不能满足为止,那么就可以找到一个满足右端点dfsnx\ge{dfsn_x}的最大的左端点的区间,然后对应到这个区间的根,那么这个根节点就是fx,0f_{x,0}

所以这样我们就把从下方的节点到根这样一个单调问题解决了。

那么来看原问题,

image.png

例如就是求黑点ss到蓝点tt的最大宝石数。

那么就可以把它拆成两段来看:slca(s,t),lca(s,t)ts\to{lca(s,t)},{lca(s,t)\to{t}}

前一段我们已经会了,那么后一段其实就是它的对称情况,我们定义gi,jg_{i,j}代表从ii号点开始扔出jj个宝石的数,假设第ii号点的宝石种类为pkp_k,那么gi,0g_{i,0}对应的就是上一个最近的第pk1p_{k-1}种宝石的位置,那么剩下的处理方式就和之前一样了,那么最后把slca(s,t)s\to{lca(s,t)}ff值和tlca(s,t)t\to{lca(s,t)}gg值加起来就是所求答案,它们可以使用同一个 ST 表。

所以我们就可以进行一个二分答案,二分它最大能得到的宝石数为xx,假如终点处恰好为第xx种,那么就可以直接求,假如不是,我们就需要找 dfs 序小于tt且深度最大的第xx种宝石的位置。这个位置可以类似预处理f,gf,g一样求解,其实就是找在包含tt的子树中根节点的 dfs 序最大的,不懂请回看预处理。

这样就可以 check 一下在最终得到宝石数为x的情况下是否可能,设ff倍增到深度小于等于lca(s,t)lca(s,t)的最大深度为的点选了第aa种宝石,gg倍增到深度小于等于lca(s,t)lca(s,t)的最大深度的点选了第bb种宝石,假如a>=ba>=b那么就是满足的,可以继续扩大xx来二分,直到b=a+1b=a+1这种恰好成立的条件。

最后还要特判一下是否同时跳到了最近公共祖先节点,如果是就还要1-1,因为同一颗宝石被选了两次。