您的位置:首页 > 理论基础 > 数据结构算法

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

2014-03-25 19:38 211 查看
题目链接:http://codeforces.com/problemset/problem/375/D

全部代码:https://github.com/9974/DataStructures/tree/master/375D

题意:给你一棵树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))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息