本题有两种算法,一种是 log n \log n logn 级别算法,一种是 n \sqrt{n} n 级别算法,这里只讲根号算法。
这道题就是一个树上数颜色问题,只是颜色被限定在一个区间。
如果你做过 P4396 [AHOI2013]作业,我相信你能一眼看出这道题的做法。
数颜色问题的根号算法做法一般是莫队,而这道题是树上莫队。
如果你没学过莫队,看一下我的算法总结。
如果你没学过树上莫队但是学过莫队,可以看我的算法总结,也可以在代码后面看我写的简要描述。
好的下面默认你学会了树上莫队。
那么有一种方法就是我们在树上莫队的时候维护一棵线段树,对于加删操作而言我们在线段树对应位置修改,线段树中维护当前区间是否存在奇数个颜色,查询时直接查即可。
这个做法的复杂度是 O ( q log n n + q log n ) O(q \log n \sqrt{n}+q \log n) O(qlognn+qlogn) 的,会被 #7 卡 TLE。
发现修改操作复杂度 q log n n q \log n \sqrt{n} qlognn 远大于查询操作复杂度 q log n q \log n qlogn,因此我们需要考虑平衡一下这两者。
因此我们可以采用值域分块。
如果你没学过值域分块,建议先去学一下分块(我的算法总结),然后类比值域线段树,值域分块就是在值域上分块。
值域分块有一个好处是 O ( 1 ) O(1) O(1) 修改 O ( n ) O(\sqrt{n}) O(n) 查询,而这可以平衡复杂度,使得修改操作和查询操作都是 O ( q n ) O(q\sqrt{n}) O(qn)。
需要注意的是如果块长不当,可能会被 #7 卡掉,这里建议调成理论最优块长 2 n m \dfrac{2n}{\sqrt{m}} m2n( 2 n 2n 2n 是因为欧拉序长为 2 n 2n 2n),如果还不过就在这附近调块长。
Code:GitHub CodeBase-of-Plozia CF1479D Odd Mineral Resource.cpp
树上莫队简介:
做树上莫队,你需要知道欧拉序。
欧拉序就是 DFS 序的升级版,在每一次遍历到这个节点的时候记录一次,离开这个节点的时候再记录一次。
一般采用 f i r x fir_{x} firx 表示 x x x 在欧拉序中第一个出现的位置, l a s x las_{x} lasx 表示 x x x 在欧拉序中第二个(最后一个)出现的位置。
当一个询问询问路径 x → y x \to y x→y 的时候,如果 x , y x,y x,y 在一条链上,询问的区间就是 [ f i r x , f i r y ] [fir_x,fir_y] [firx,firy],否则就是 [ l a s x , f i r y ] [las_x,fir_y] [lasx,firy],具体询问区间可以使用 LCA 判断。
需要注意的是当询问 [ l a s x , f i r y ] [las_x,fir_y] [lasx,firy] 的时候,不能忘记计算他们 LCA 的贡献。
由于一个点在欧拉序中会出现两次,因此树上莫队的修改采用奇增偶删原则,即若这个点是奇数次被修改就增加,偶数次被修改就减少。
写的有点少大致看看吧qwq