Codeforces Div2 864
Codeforces Round #864 (Div. 2)
D. Li Hua and Tree
题目大意
李华有一棵$n$个节点的有根树,根节点为$1$,第$i$个节点有点权$a_i$
定义一个非叶子节点的重儿子为其所有儿子里子树大小最大的,若有多个大小相同的则取编号最小的
维护$m$次操作
操作一:求以$x$为根的子树的点权和
操作二:记$x$的父亲为$fa_x$,重儿子为$hs_x$,切断$(x, fa_x)$之间的边,新加一条$(hs_x, fa_x)$之间的边
操作二不保证$x$是叶子节点,若$x$是叶子,就忽略该操作
解题思路
操作二是一个很局部的操作,改变的信息并不多
记$x$的父亲为$f$,重儿子为$v$
对于$f$,这些信息在操作二后可能改变:$hs_f,sons_f$
对于$v$,这些信息在操作二后可能改变:$hs_v,sons_v, sum_v,sz_v,fa_v$
对于$u$,这些信息在操作二后可能改变:$hs_u,sons_u, sum_u,sz_u,fa_u$
每个信息都能以$O(1)$或$O(log)$的复杂度修改
只要注意修改顺序即可
具体代码
1 |
|
E. Li Hua and Array
题目大意
$\phi(n)$是$n$的欧拉函数,值为小于等于$n$的,且与$n$互质的正整数个数
李华有一个正整数序列$a$,维护$m$个操作
操作一:对区间$[l,r]$进行区间修改,每个$a_i$变成$\phi(a_i)$
操作二:对于区间$[l,r]$,回答最小修改几次能使得$a_l=a_{l+1}=\cdots=a_r$,每次修改能选中一个$x\in[l,r]$,把$a_x$变成$\phi(a_x)$,只需要回答最小修改次数,并不用真的修改序列
解题思路
考虑另一个类似的问题
操作一:对区间$[l,r]$进行区间修改,每个$a_i$变成$\lceil{\frac{a_i}{2}}\rceil$
操作二:对于区间$[l,r]$,回答最小修改几次能使得$a_l=a_{l+1}=\cdots=a_r$,每次修改能选中一个$x\in[l,r]$,把$a_x$变成$\lceil{\frac{a_x}{2}}\rceil$,只需要回答最小修改次数,并不用真的修改序列
可以发现一个数经过最多$log$次操作一,都会变成$1$并且之后不再改变
考虑建一棵树,对于每个$a_i$,在$a_i$和$\lceil{\frac{a_i}{2}}\rceil$之间建边,$\lceil{\frac{a_i}{2}}\rceil$作为$a_i$的父亲,类似地一层一层往上建,最后一定会变成一个根为$1$的树
此时再看操作二,答案是区间$[l,r]$内所有点跳到同一个点所需的最小次数,其实就是这些点到他们的$lca$的距离之和
首先$lca$先用倍增预处理好,然后考虑用线段树解决这个问题
对于操作一,线段树需要维护的有:区间深度之和$sum$
维护这个主要是因为当一个数变为$1$后,深度不能再变了
同时可以发现这里区间修改并没有使用懒标记,而是直接暴力递归到叶子修改,为什么这样可行呢$?$ 原因放在最后
对于操作二,线段树需要维护的有:区间$lca$,每个区间对操作二的答案$ans$,区间长度$len$
区间$lca$与区间$len$不用赘述
解释一下下面这个对$ans$的$pushup$
1 | ans[id] = ans[id << 1] + ans[id << 1 | 1] + len[id << 1] * (dep[lca[id << 1]] - dep[lca[id]]) + len[id << 1 | 1] * (dep[lca[id << 1 | 1]] - dep[lca[id]]); |
当前答案由四部分组成,左儿子区间的答案,右儿子区间的答案,左儿子区间$lca$到当前区间$lca$的距离乘以左儿子区间$len$,右儿子区间$lca$到当前区间$lca$的距离乘以右儿子区间$len$
$query$本质上也是一个类似的$pushup$
由于欧拉函数与除以二再上取整是类似的
根据此题$a_i$的值域,一个数最多经历$23$次欧拉函数就会到$1$$($可以自己打表实验$)$
所以只要按照上述思路,再套个欧拉函数的壳子即可
为什么可以递归到叶子暴力修改:
考虑一个数最多经历$20$多次欧拉函数就会到$1$这个性质
再考虑即使每次操作一都修改$[1,n]$这个最长的区间
也最多执行$20n$次,
然后$sum$就会变成$0$,之后再操作一就会及时$return$回去
对于本题也就是$2e6$次,完全可以接受
具体代码
1 |
|