[CF1616H] Keep XOR Low 题解
首先看到两两异或问题,考虑先把集合放在字典树上。
本着求什么设什么的原则,我们设 \(f_x\) 表示 \(x\) 的子树里满足要求的集合数,然后我们来试着转移一下:
- 当前位为 \(0\) 时,我们不能只能在同侧子树中选,则 \(f_x=f_{x_0}+f_{x_1}\)(\(x_0,x_1\) 分别表示 \(x\) 的左右儿子编号)。
- 当前位为 \(1\) 时,我们同侧子树中选择没有约束,但是异侧子树之间存在约束,而我们的 dp 状态只能接受一棵子树内部约束,所以得考虑重设状态。
我们重新设一个合理但又不合理的状态,考虑到当前位为 \(1\) 时会产生异侧子树之间的互相约束,我们设 \(f_x\) 表示 \(x\) 子树里满足要求的集合数,再额外设一个 \(g_{x,y}\) 表示 \(x,y\) 子树里满足要求的集合数(强制要求 \(x\ne y\)),我们可以再考虑转移。
- 若当前位为 \(0\):
- 若当前位为 \(1\):
答案是 \(f_{\mathrm{root}}-1\)。
我们来一个一个解释转移方程。
首先是 \(f_x\) 的转移,当前位为 \(0\) 时,我们不能在异侧子树中选数,故要么从左子树选,要么从右子树选,故两边加和,同时因为加和的过程中空集被算了两次。当前位为 \(1\) 时,同侧子树中选数没有要求,异侧子树互相约束,根据定义即 \(g_{x_0,x_1}\)。
再来看 \(g_{x,y}\) 的转移,首先我们应当明确一点,\(g_{x,y}\) 的询问中,\(x,y\) 子树内部选点之间不存在约束,这样的话两个转移也就很明显了,当前位为 \(0\) 时,\(x\) 与 \(y\) 的异侧不能同时选点,所以是分别将同侧选点的方案加起来,同样的减去 \(1\) 的空集贡献,后面部分,因为我们在询问 \(g_{x_0,y_0}\) 和 \(g_{x_1,y_1}\) 时还会再统计\(x_0,x_1,y_0,y_1\) 单个子树内部选点的方案数,所以要保证当前我们统计时不存在只在单侧选点的情况,故统计时还需加上 \((2^{\mathrm{size}_{x_0}}-1)(2^{\mathrm{size}_{x_1}}-1)+(2^{\mathrm{size}_{y_0}}-1)(2^{\mathrm{size}_{y_1}}-1)\)。
时间复杂度分析方面,我们发现对于每个节点 \(x\),它只会被 \(f_x\) 遍历或存在一个唯一的 \(y\) 被 \(g_{x,y}\) 遍历,故时间复杂度正确,为 \(\mathcal O\left(n\log|V|\right)\)。
代码就相对简单多了。