寒武纪camp网络测试赛
地址:oj点我进入
A(树形dp+树链剖分)
题意:
分析:
考虑树形dp,f0(x)和f1(x)分别表示以x为根的子树,不取x点或取x点的最大合法子集的元素个数
那么对于一个询问来说,答案就是max(f0(x),f1(x)),我们的任务就是对于每次修改操作维护所有f数组的变化
假设我们新加入了一个x节点,很明显只有x以及x的祖先们的f值会改变
首先f1(x)=f0(x)+1,因为我多取一个x节点一定是合法的(题目保证权值从小到大)
然后考虑x的祖先fa,应该有f0(fa)=f0(fa)+1,因为fa的子树里面可以多选一个点x
但问题是x的某个祖先fa可能f0值不会变,因为它的最优决策可能是某个f1(y),其中dep(fa)<dep(y)<dep(x) 且三个在一条链上,所以y及y以上部分的f值都不会改变
那么什么时候会出现这种情况呢,其实就是f1(y)>=f0(y)+1这时候,因为这时候我选择“放y这个点”不比“不放y这个点且放刚进入的x点”劣
于是我们想到了很自然的维护方法,对于每次新来的点x,一直向上跳,跳到第一个f1(y)>=f0(y)+1的位置停止,将father(x)~y这一段的点的f0全部加上1
那当然这样会TLE,我们只能用数据结构来维护这样的事
在树上给一条链加上1这件事很自然可以想到用树链剖分+线段树去做,问题是如何找到最近的那个f1(y)>=f0(y)+1的点
我们发现这样的y只可能是目前的所有C操作里面的所有点,即目前有赋权值的点,而不是其它的未赋权值的点,换句话说我们的给一段点的f0全部加上1这样的操作不会在加的过程中产生新的y,也就是说加1操作和维护这样的y是独立的
于是我们可以把所有的f1(y)>=f0(y)+1打个标记,对于每个新加入的x,我们在树上找到离x最近的带标记的祖先y,然后将这一段+1,同时将y的标记消除(因为y已经变成了f1(y)=f0(y)了)
那么怎么找最近的带标记的祖先y呢,我们可以顺着重链向上跳,然后每次在线段树上找最靠右边的标记节点,这样是O(logn * logn)的
所以总的时间复杂度就是$O(nlog^2n)$的