题目链接: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 $\log n$ times.”
发现merge的时候大集合至少是小集合的2倍,实际上就是说每次每个集合被遍历的次数都会减半,相当于最坏情况下每个点被遍历$\log n$次,于是merge的复杂度就是$n\log n $. 整体复杂度就是$O(n\log^2 n)$.
1 |
|