CF601D Acyclic Organic Compounds
给出一棵 \(n\) 个点的有根树,每个点上有一个字符 \(s_i\) 和权值 \(c_i\)。
定义 \(str(u,v)\) 为 \(u\) 到 \(v\) 路径上所有点上的字符顺次拼接得到的字符串。
定义 \(\text{dif}(u)\) 为在 \(u\) 子树内任选一个点 \(v\),可以得到多少种本质不同的 \(str(u,v)\)。
求 \(\max\limits_{i=1}^n (\text{dif}(u)+c_u)\),以及多少个点取到最大值。
\(n\le 3\times 10^5\)。
首先本质不同的 \(str(u,v)\) 等价于本质不同的 \(str(v,u)\)。考虑树上启发式合并,用 std::set 维护每个子树内有多少种本质不同的哈希值。
但是这题又不太一样,因为每个点继承重儿子时,重儿子内的所有 \(str(v,u)\) 都要拼上当前点的字符,因此哈希值需要整体乘 / 加。
考虑维护 \(mul,add\) 两个标记,表示 std::set 里的元素要乘上 \(mul\),再加上 \(add\) 才能得到真实值。那么我们 std::set 里维护的值,就是与标记运算后能得到真实值的数(称为 \(base\))。那么有几种真实值,就是有几种 \(base\)。遍历轻儿子子树插入 \(base\) 就可以得到当前子树的 std::set。
由于轻儿子子树内的点是不需要与 \(tag\) 运算的,因此它们的 \(base\) 就是要“撤销”标记,再与标记运算,相当于不变,即真实值。所以还需要求出 \(mul\) 的逆元来撤销。
还要注意标记的合并,比如我先乘 \(a\) 加 \(b\),再乘 \(c\) 加 \(d\),那么对于一个数 \(x\) 而言,它会变成 \(c(ax+b)+d=acx+bc+d\),此时 \(mul\) 变成 \(ac\),\(add\) 变成 \(bc+d\)。
时间复杂度为 \(\mathcal{O}(n\log^2 n)\)(应该可以用哈希表做到 \(\mathcal{O}(n\log n)\)),空间复杂度为 \(\mathcal{O}(n)\)。注意要写双哈希。

浙公网安备 33010602011771号