hdu-4358:Boring counting(优美算法之树上启发式合并)

 

通过这道题了解到了神奇的树上dsu算法,起因是最近想再学习一波线段树,然后就找了一套线段树总结的题目。在那个总结里面看到了这道题,感觉很有意思。然后晚上回了宿舍就和室友在讨论,然后室友告诉我树上启发式合并随便做???

当时一脸懵逼,树上启发式合并是什么鬼,其实个人对启发式合并还是有一点了解的。之前做了一道可持久化并查集,那里面并查集的合并没有用正常的路径压缩,是用判断两个并查集大小的方式进行合并,最后保证复杂度维持在nlogn。

但是树上启发式合并之前完全没有听说过。室友信誓旦旦的说这做法可以解决大部分的子树查询问题。表示怀疑,因为我最开始想到的解法其实是dfs序之后直接莫队。线段树我们都不懂怎么写= = 然后一起研究了一下线段树的写法,感觉确实太麻烦了,室友说他第二天要用dsu解决这道题。我第二天用了莫队A了,他用dsuA了。。。于是我就把他的资料拿过来研究了一下,发现树上dsu是真的神奇,搞懂以后就感觉是个纯暴力但是稍加修改后它的复杂度是nlogn!!!

但是这个算法的局限性也比较大,首先好像不能插入修改操作,其次的话只能应对子树的查询问题。而且怎么说呢,如果要查询的东西比较复杂的话感觉也比较难搞。

不过就算如此,也是一个非常优秀的算法了,在应对子树查询类问题的时候可以优先考虑一下。而且,技多不压身啊。。。

贴一下树上dsu的学习资料:

原文链接:CF原文

推荐博客:https://blog.csdn.net/QAQ__QAQ/article/details/53455462

推荐博客:https://www.cnblogs.com/zzqsblog/p/6146916.html

 

 

题目大意:

给你一颗树,每个结点有自己的权值,求权值恰好出现次数为k的个数。

 

解题思路:

dfs序后直接莫队或者线段树都可,这里用树上dsu来写 = = 

 

Ac代码:

#include
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,k,m,res,a[maxn],sum[maxn],sz[maxn],son[maxn],q[maxn],ans[maxn];
bool vis[maxn];
vector v[maxn],rv;
int getid(int x) { return lower_bound(rv.begin(),rv.end(),x)-rv.begin()+1; }
void init()
{
    rv.clear();
    for(int i=0;i<=n;i++) v[i].clear();
    for(int i=0;i<=n;i++) sum[i]=son[i]=vis[i]=0;
}
void dfs1(int x,int fa) //求出每个结点的重儿子
{
    sz[x]=1;
    for(int i=0;isz[son[x]]) son[x]=u;
    }
}
void edit(int x,int fa,int sp)  //更新每个点的贡献
{
    if(sum[a[x]]==k) res--;
    sum[a[x]]+=sp;
    if(sum[a[x]]==k) res++;
    for(int i=0;i

 

你可能感兴趣的:(启发式合并)