Codeforces 375D 数据结构(好题中的好题, 4解)

2014-11-24 11:19:09 · 作者: · 浏览: 0

题意:给你一棵树n个点,m次询问(n=100000,m=100000),每个节点有一种颜色,

每次询问问你以v节点为根的子树中 满足 同一种颜色的个数>=k的 颜色有几个。

方法1:显然询问要离线处理,不妨用思维简单的分块算法处理询问,

对于每个询问,我们用数组val[i]表示当前情况下 颜色为i的节点个数

再用val[i]当做下标,用数状数组维护个数的后缀和,

每次修改一个点 树状数组里面更新2次, 总体复杂度O(sqrt(n)*n*log(n));

方法2: 其实也算方法1的优化, 不同之处只是用一个数组维护后缀和,

我们发现 每次修改无非就是把val[i]加一或者减一,不妨用数组sum表示要求的答案

加1:唯一改变的是 val[i]+1的答案, 这个答案要加1, 即sum[++val[i]]++;

减1:唯一改变的是 val[i]的答案, 这个答案要减1, 即sum[val[i]--]--;

总体复杂度O(sqrt(n)*n);

方法3:启发式合并, 询问按节点v分类,从叶子节点不断合并,注意这里一定要把小的堆合并到大的堆里面,

每个子树用一个平衡树维护(这里我用了treap),用另外一个平衡树维护每个节点u为根的子树的所有颜色个数

(我用了map)合并过程就是把两棵平衡树合并, treap里面维护的是val[i],要求答案只要算一下后缀和就可以了

总体复杂度O(n*log^2(n))

方法4: 分治思想, 同样询问按节点v分类, 想想暴力的做法处理询问,用一个全局的数组维护答案,发现O(n^2)可做

但是会超时, 我们可以做个优化, 对于根u 处理 其子树v1,v2,v3....时, 我们算子树答案的时候,

把v1子树放到答案数组里, 算完以后清空v1子树, 然后在重复v2,v3....., 注意到最后一个子树没有必要清空,

算完最后一个v后dfs会回溯到上一层,去算上一层的答案,而上一层一定包含了v子树, 清空了反而复杂度会大大提升,

所以我们当然是把子树规模最大的放到最后去处理比较优秀,这里类似熟练剖分的重链,

每次把子树加进来和删除另外写两个dfs暴力加减,这样平均下来每个点被添加和删除的次数就不会超过log(n),

这样一来总体复杂度为O(n*log(n))