dsu on tree的本质是树上的启发式合并,它利用启发式合并的思想,可以将O(N^2)的暴力优化成O(NlogN),用于不带修改的子树信息查询。

  具体如何实现呢?对于一个节点,继承它重儿子的信息,轻儿子直接dfs统计,更新完本节点的答案后,再dfs一次清除轻儿子的信息,相当于一个启发式合并的过程,因为一次合并会使得被遍历的子树变大一倍,所以一棵子树最多遍历logn次,也就是一个点最多被遍历logn次,于是最劣复杂度为O(NlogN)。

  dsu on tree的具体流程

    dfs计算轻儿子的答案,(有时候也需要继承轻儿子的答案)

    dfs计算重儿子的答案,继承重儿子的答案(有时候不需要)

    dfs计算轻儿子的贡献

    更新当前结点的答案

    dfs删去轻儿子的贡献

模板代码:

void dfs1(int x)
{
size[x]=;
for(int i=last[x];i;i=e[i].pre)
dfs1(e[i].too),size[x]+=size[e[i].too],size[e[i].too]>size[son[x]]&&(son[x]=e[i].too);
}
void update(int x,int fa,int delta)
{
// 更新信息
for(int i=last[x];i;i=e[i].pre)
if(e[i].too!=fa&&e[i].too!=skip)update(e[i].too,x,delta);
}
void dfs2(int x,int fa,bool heavy)
{
for(int i=last[x];i;i=e[i].pre)
if(e[i].too!=fa&&e[i].too!=son[x])dfs2(e[i].too,x,);
if(son[x])dfs(son[x],x,),skip=son[x],ans[x]=ans[son[x]];//有时不需要继承
update(x,fa,);skip=;
ans[x]+=....//有时在update里更新答案
if(!heavy)update(x,fa,-);
}

  例1 CF600E

  题目大意:统计每一个子树里众数的和(可以有多个众数)

  记录每种颜色的出现次数,然后就是模板题了...

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=,inf=1e9;
struct poi{int too,pre;}e[maxn];
int n,x,y,tot,zs,skip;
int last[maxn],col[maxn],cnt[maxn],son[maxn],size[maxn];
ll sum,ans[maxn];
inline void read(int &k)
{
int f=;k=;char c=getchar();
while(c<''||c>'')c=='-'&&(f=-),c=getchar();
while(c<=''&&c>='')k=k*+c-'',c=getchar();
k*=f;
}
inline void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
void dfs1(int x,int fa)
{
size[x]=;
for(int i=last[x],too;i;i=e[i].pre)if((too=e[i].too)!=fa)
dfs1(too,x),size[x]+=size[too],size[too]>size[son[x]]&&(son[x]=too);
}
void update(int x,int fa,int delta)
{
cnt[col[x]]+=delta;
if(delta>&&cnt[col[x]]>zs)zs=cnt[col[x]],sum=col[x];
else if(delta>&&cnt[col[x]]==zs)sum+=col[x];
for(int i=last[x];i;i=e[i].pre)
if(e[i].too!=fa&&e[i].too!=skip)
update(e[i].too,x,delta);
}
void dfs(int x,int fa,bool heavy)
{
for(int i=last[x];i;i=e[i].pre)
if(e[i].too!=fa&&e[i].too!=son[x])dfs(e[i].too,x,);
if(son[x])dfs(son[x],x,),skip=son[x];
update(x,fa,);ans[x]=sum;skip=;
if(!heavy)update(x,fa,-),zs=sum=;
}
int main()
{
read(n);
for(int i=;i<=n;i++)read(col[i]);
for(int i=;i<n;i++)read(x),read(y),add(x,y),add(y,x);
dfs1(,);dfs(,,);
for(int i=;i<=n;i++)printf("%lld ",ans[i]);
}

  例2 CF741D

  题目大意:每一条边有一个字符,求每一个子树里最长的路径使得路径上的字符经过重新排列可以成为回文串

  这题有点像上场atcoder的D题...因为一个字符串经过重新排列可以成为回文串当且仅当每种颜色的出现次数为偶数或者只有一种颜色的出现次数为奇数,于是可以想到利用异或来判断。将每一种字符映射到二进制上的一位(1<<(ch-'a)),预处理出点到根节点的路径上的异或值,那么判断两个节点的路径上是否可以经过重新排列成为回文串就是st[x]^st[y]是不是0或者2的幂。

  所以这题我们只需要记录状态为i的最深深度,然后就又是模板题了。

  注意状态为i的如果没有要设为-inf,否则会GG (调了一天T T

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=,inf=1e9;
struct poi{int too,dis,pre;}e[maxn];
int n,x,y,tot,skip,sum;
int st[maxn],col[maxn],size[maxn],dep[maxn],son[maxn],mx[<<],last[maxn],ans[maxn];
char ch[];
inline void read(int &k)
{
int f=;k=;char c=getchar();
while(c<''||c>'')c=='-'&&(f=-),c=getchar();
while(c<=''&&c>='')k=k*+c-'',c=getchar();
k*=f;
}
inline void add(int x,int y,int z){e[++tot].too=y; e[tot].dis=z; e[tot].pre=last[x]; last[x]=tot;}
void dfs1(int x,int fa)
{
size[x]=;
for(int i=last[x],too;i;i=e[i].pre) if((too=e[i].too)!=fa)
{
dep[too]=dep[x]+;
st[too]=st[x]^(<<e[i].dis);
dfs1(too,x);
size[x]+=size[too];
if(size[too]>size[son[x]]) son[x]=too;
}
}
void Count(int x,int fa,int f)
{
sum=max(sum,mx[st[x]]+dep[x]-*dep[f]);
for(int i=;i<;i++) sum=max(sum, mx[st[x]^(<<i)]+dep[x]-*dep[f]);
if(x==f)return;
for(int i=last[x],too;i;i=e[i].pre)
if((too=e[i].too)!=fa && too!=skip) Count(too,x,f);
}
void update(int x,int fa,int delta)
{
if(delta>) mx[st[x]]=max(mx[st[x]],dep[x]);
else mx[st[x]]=-inf;
for(int i=last[x],too;i;i=e[i].pre)
if((too=e[i].too)!=fa && too!=skip) update(too,x,delta);
}
void dfs2(int x,int fa,bool heavy)
{
for(int i=last[x],too;i;i=e[i].pre)
if((too=e[i].too)!=fa && too!=son[x])
dfs2(too,x,), ans[x]=max(ans[x],ans[too]);
if(son[x]) dfs2(son[x],x,), skip=son[x], ans[x]=max(ans[x],ans[son[x]]);
sum=; Count(x,fa,x); mx[st[x]]=max(mx[st[x]],dep[x]);
for(int i=last[x],too;i;i=e[i].pre)
if((too=e[i].too)!=fa && too!=skip)
Count(too,x,x), update(too,x,);
ans[x]=max(ans[x],sum); skip=;
if(!heavy) update(x,fa,-);
}
int main()
{
read(n); for(int i=;i<(<<);i++) mx[i]=-inf;
for(int i=;i<=n;i++) read(x), scanf("%s",ch), y=ch[]-'a',add(x,i,y);
dfs1(,); dfs2(,,);
for(int i=;i<=n;i++) printf("%d ",ans[i]);
}

  例3 NOIP模拟赛10.23 T2

【算法】dsu on tree初探的更多相关文章

  1. [算法学习] dsu on tree

    简介 dsu on tree跟dsu没有关系,但是dsu on tree借鉴了dsu的启发式合并的思想. 它是用来解决一类树上的询问问题,一般这种问题有以下特征: \(1.\)只有对子树的查询: \( ...

  2. [dsu on tree]【学习笔记】

    十几天前看到zyf2000发过关于这个的题目的Blog, 今天终于去学习了一下 Codeforces原文链接 dsu on tree 简介 我也不清楚dsu是什么的英文缩写... 就像是树上的启发式合 ...

  3. dsu on tree入门

    先瞎扯几句 说起来我跟这个算法好像还有很深的渊源呢qwq.当时在学业水平考试的考场上,题目都做完了不会做,于是开始xjb出题.突然我想到这么一个题 看起来好像很可做的样子,然而直到考试完我都只想出来一 ...

  4. [学习笔记]Dsu On Tree

    [dsu on tree][学习笔记] - Candy? - 博客园 题单: 也称:树上启发式合并 可以解决绝大部分不带修改的离线询问的子树查询问题 流程: 1.重链剖分找重儿子 2.sol:全局用桶 ...

  5. dsu on tree题表

    dsu on tree,又名树上启发式合并.重链剖分,是一类十分实用的trick,它常常可以作为一些正解的替代算法: 1.DFS序+线段树/主席树/线段树合并 2.对DFS序分块的树上莫队 3.长链剖 ...

  6. DSU on Tree浅谈

    DSU on tree 在之前的一次比赛中,学长向我们讲了了这样一个神奇的思想:DSU on tree(树上启发式合并),看上去就非常厉害--但实际上是非常暴力的一种做法;不过暴力只是看上去暴力,它在 ...

  7. 【CodeForces】741 D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)

    [题意]给定n个点的树,每条边有一个小写字母a~v,求每棵子树内的最长回文路径,回文路径定义为路径上所有字母存在一种排列为回文串.n<=5*10^5. [算法]dsu on tree [题解]这 ...

  8. 【CodeForces】600 E. Lomsat gelral (dsu on tree)

    [题目]E. Lomsat gelral [题意]给定n个点的树,1为根,每个点有一种颜色ci,一种颜色占领一棵子树当且仅当子树内没有颜色的出现次数超过它,求n个答案——每棵子树的占领颜色的编号和Σc ...

  9. dsu on tree总结

    dsu on tree 树上启发式合并.我并不知道为什么要叫做这个名字... 干什么的 可以在\(O(n\log n)\)的时间内完成对子树信息的询问,可横向对比把树按\(dfs\)序转成序列问题的\ ...

随机推荐

  1. normalization flow

    from Eric Jang Normalizing flows transform simple densities (like Gaussians) into rich complex distr ...

  2. 【转载】JS导出CSV文件

    转自:http://www.cnblogs.com/dengnan/p/3990211.html 通过自己实际测试有以下几种方法 方法一通过a标签实现,把要导出的数据用“\n”和“,”拼接成一个字符串 ...

  3. HashMap源码(一)

    本文主要是从学习的角度看HashMap源码 HashMap的数据结构 HashMap是一个数组+链表的结构(链表散列),每个节点在HashMap中以一个Node存在: HashMap的初始化 publ ...

  4. uipath_excel

    1.excel建表 https://jingyan.baidu.com/article/95c9d20d0ee5e2ec4e75618d.html 2.具体操作 https://blog.csdn.n ...

  5. 少年的u

    发现了我提取DNa的过程存在问题,能够跑出action 但是不能克隆出基因.老师给我解释了为什么,说是我的DNA质量不是很高.但是在接下来的时间我会解决这个问题. 和师姐一起去上面的实验室,看了定量P ...

  6. ORA-01476: 除数为 0

    假设是a/bdecode(b,0,null,a/b) 这样如果b为0,输出null,不为0输出a/b decode():将查询结果翻译成其他值,类似三目运算符 比较1个参数时      decode( ...

  7. NG-ALAIN 边学边记2

    1. 下载Git上的源码  : https://github.com/ng-alain/ng-alain.git 2.解压文件: 3.进入到目录下: 4.打开CMD 切换到 E:\NgAlain\ng ...

  8. winform常用控件介绍

    1.窗体 12.Label 控件 33.TextBox 控件 44.RichTextBox控件 55.NumericUpDown 控件 76.Button 控件 77.GroupBox 控件 78.R ...

  9. STM32------SYSCLK

    重要的时钟:PLLCLK,  SYSCLK,  HCKL,   PCLK1,   PCLK2  之间的关系要弄清楚; 1.HSI:高速内部时钟信号 stm32单片机内带的时钟(8M频率)精度较差2.H ...

  10. 1.game.h

    game.h //以后会一直更新的呢. /*2020.2.4 更新了文件输入输出模板 @Bill_liu2020 加入了visual.h @bingogyz(orz) 代码:https://blog. ...