题目链接:http://codeforces.com/contest/600/problem/E
给一棵树,问从根节点开始每个节点的子树(包含这个节点)中的众数的和。
一看到子树上的某种计数我就会非常自然地想到树转RMQ再做一些区间操作,当然这个题也不例外。。。
于是考虑把这棵树的查询转成区间查询,要查每一个节点的子树,那么就是有n次查询。线段树是解决不了区间众数问题的,于是就想到去分块。但是这题的意思是众数如果出现多次那就要把他们都加起来,所以我们在维护每个数出现的次数的同时,还要维护每个次数对应的数字之和。更新的时候注意加加减减就可以了,还有就是每次更新当前最大次数ret的时候遇到remove要判断一下当前这个出现次数是否已经没有数字了,遇到这种情况的时候,因为每次出现次数都-1,于是我们直接把ret−1就行。
1 |
|
这题其实就是想教大家在维护每个数出现的次数的同时,还要维护每个次数对应的数字之和。我们冷静分析以后,发现这个题可以直接DFS。
我们希望在每次DFS的时候把子树中的数字出现情况merge到当前这个父亲上,考虑对每个点维护上述两个数组,每次把小的树更新到大的树上,merge的具体操作跟上述莫队的更新一样。
如何保证小集合merge到大集合上的复杂度?
Tutorial是这么解释的:“every time when vertex v will be moved from one map to another the size of the new map will be at least two times larger. So each vertex can be moved not over than logn times.”
发现merge的时候大集合至少是小集合的2倍,实际上就是说每次每个集合被遍历的次数都会减半,相当于最坏情况下每个点被遍历logn次,于是merge的复杂度就是nlogn. 整体复杂度就是O(nlog2n).
1 |
|
v1.5.2