LG-P3603 雪辉 Solution

更好的阅读体验戳此进入

题面

给定 n 个点的树,存在点权,多次询问每次给定多对点分别表示一条树链,求所有树链中有多少不同点权,及其点权的 mex。强制在线。

Solution

首先看到计算不同点权和求 mex 且值域不大,自然想到 bitset,当我们求得答案的 bitset,我们只需要调用 count() 即可获得点权种类数,将其取反后,调用 _Find_first() 即可获得 mex。且上述所有操作均会除去一个 bitset 特有的,w 的复杂度。

这里的 w 根据编译器版本一般为 3264,且这里记作 w 而不是 ω 是因为个人认为理解为 word 较为合理。

考虑如何维护,首先提供一个十分显然的思路,对原树进行树剖,然后在线段树上每个节点均开一个 bitset 建树,令 v 表示点权,这样的复杂度显然是 O(nvw) 的,而对于每次询问,直接查询并强行合并,分析这样的复杂度:树剖有一只 O(logn),线段树上查询的节点数是 O(logn),每次合并需要 O(vw),若共 q 次询问则最终复杂度 O(qvlog2nw),显然无法通过,但是本题似乎限制较小,这样也可以通过。

考虑分析上述做法的空间复杂度,显然为 O(nvw),但是线段树一般有一个 4 的空间常数,简单计算发现精细实现后大致需要 262413,这样大致需要 1GiB,考虑优化,显然对于线段树的底层每个点仅维护了一个数,而倒数第二层每个点仅维护了两个数,于是不难想到我们将倒数这两层改为用 pair 维护即可,这样可以去掉 131072+65536 左右个 bitset 的空间复杂度,优化极大,空间冗余较多,实现平凡。

下面提供一个来自 @Zpair 的高妙思路,可以将复杂度去掉一只 O(logn),显然这样的复杂度就是理论正确的了。即考虑在树剖后每个重链上维护这整个重链的 bitset,同时类似上文维护线段树,对于每次询问中,所有整个重链的查询直接调用,容易证明残缺的重链查询最多有 3 次,可以认为是常数,故优化掉一只 O(logn),容易证明这样的复杂度在当前时空限制是可以通过的。同时若空间无法通过还可考虑对于所有点数小于 vw 的直接用 basic_string 维护,这样可以更进一步地大幅优化空间。

当然这里因为前者虽然复杂度错误但常数不大可以通过,于是这里直接挂前者的代码了。

Code

UPD

update-2023_02_17 初稿