首页 > 其他分享 >【学习笔记】DSU on Tree

【学习笔记】DSU on Tree

时间:2023-08-22 09:12:57浏览次数:44  
标签:遍历 int void DSU Tree 笔记 fa 子树 节点

概述

DSU on Tree 即树上启发式合并,重点不在“合并”,而在利用树链剖分的性质对子树问题进行复杂度正确的分治。

算法流程

  1. 递归处理轻儿子的答案

  2. 递归处理重儿子的答案

  3. 重新遍历轻儿子子树,计算当前子树的答案

  4. 如果当前节点是轻儿子,重新遍历整棵子树,清除答案

发现一个节点被再次遍历当且仅当其所在的子树根为轻儿子,在这之后其所在子树大小至少扩大 \(2\) 倍,因此每个节点最多被遍历 \(O(\log n)\) 次,那么总遍历次数是 \(O(n\log n)\),若单个节点计算答案复杂度 \(O(k)\),总复杂度 \(O(kn\log n)\)。

实现如下:

void dfs1(){
    // 求出重儿子
}
void add(){
    // 加入一个节点的贡献
}
void del(){
    // 删去一个节点的贡献
}
void insert(int u){
    // 加入整棵子树的贡献
    add(u);
    for(int i=head[u],v;i;i=e[i].nxt){
        v=e[i].to;
        if(v==fa[u]) continue;
        insert(v);
    }
}
void erase(int u){
    // 删去整棵子树的贡献
    del(u);
    for(int i=head[u],v;i;i=e[i].nxt){
        v=e[i].to;
        if(v==fa[u]) continue;
        erase(v);
    }
}
void dfs2(){
    // 递归处理轻儿子答案
    for(int i=head[u],v;i;i=e[i].nxt){
        v=e[i].to;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v);
    }
    // 递归处理重儿子答案
    if(son[u]) dfs2(son[u]);
    // 重新遍历轻儿子子树,计算当前子树的答案
    add(u);
    for(int i=head[u],v;i;i=e[i].nxt){
        v=e[i].to;
        if(v==fa[u]||v==son[u]) continue;
        insert(v);
    }
    // 如果当前节点是轻儿子,重新遍历整棵子树,清除答案
    if(fa[u]&&son[fa[u]]!=u) erase(u);
}

例题

CodeForces-600E Lomsat gelral *2300

维护当前最多出现次数以及对应元素之和即可。

CodeForces-570D Tree Requests *2200

一个判断方式是最多只有一种字符出现次数为奇数,那么字符权值设计成 \(2^c\),记录一下每个深度的异或和即可轻松判断。

参考资料

标签:遍历,int,void,DSU,Tree,笔记,fa,子树,节点
From: https://www.cnblogs.com/SoyTony/p/Learning_Notes_about_DSU_on_Tree.html

相关文章

  • 「学习笔记」扩展 KMP(Z 函数)
    对于个长度为\(n\)的字符串\(s\)。定义\(z[i]\)表示\(s\)和\(s[i,n-1]\)(即以\(s[i]\)开头的后缀)的最长公共前缀(LCP)的长度。\(z\)被称为\(s\)的Z函数。这里注意,在Z函数中,\(z[0]=0\),但是根据LCP的定义,\(z[0]=n\),具体应该为何值,根据题目意思来判断。本文更偏......
  • 【Boost】boost.log 要点笔记
    常用简写:namespacelogging=boost::log;namespacesrc=boost::log::sources;namespaceexpr=boost::log::expressions;namespacesinks=boost::log::sinks;namespaceattrs=boost::log::attributes;namespacekeywords=boost::log::keywords;要点:结构图要牢......
  • [Trick] [算法学习笔记] 线段树
    事先声明:本文并非线段树教学。只是一些理解Trick。若您需从0学起线段树建议您移步其他博文呢qwq感谢Idea提供尺子姐姐的博客!,尺子好闪,拜谢尺子!我们在学习线段树的时候,对于乘法“lazytag先乘再加”是不是难以理解?这里介绍一种线段树思考方法。我们可以将序列中的每个元素......
  • Programming abstractions in C阅读笔记:p123-p126
    《ProgrammingAbstractionsInC》学习第50天,p123-p126,总结如下:一、技术总结1.notaion这也是一个在计算机相关书籍中出现的词,但有时却不是那么好理解,因为它可以指代很多对象,这里做一个记录。示例:p124。InC,youcanuseanycharacterarraytoholdstringdata.charstr[6]={......
  • 《深入理解Java虚拟机》读书笔记: 虚拟机类加载的时机和过程
    虚拟机类加载的时机和过程一、类加载的时机类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个部分统称......
  • [CF1790F] Timofey and Black-White Tree 题解
    [CF1790F]TimofeyandBlack-WhiteTree题解题目描述ZYH有一棵\(n\)个节点的树,最初\(c_0\)号节点是黑色,其余均为白色。给定操作序列\(c_1,c_2,\cdots,c_{n-1}\),第\(i\)次操作表示将\(c_i\)号节点染黑。每次操作后,输出距离最近的两个黑点间的距离。两点\(u,v\)间......
  • 操作系统笔记04
    进程管理一、进程的基本概念1、进程与程序程序是存储在磁盘上的可执行文件,程序被加载到内存中开始运行称为进程。一个程序可以同时加载多个进程,进程就是处于活动状态下的程序2、进程的分类进程根据功能不同分为三种类型:交互进程、批处理进程、守护进程交互进程:由一个shell......
  • 【学习笔记】优化建图相关(线段树优化,倍增优化)
    优化建图发现并没有人写得很详细的样子,那我也摆烂好惹点击查看目录目录前言线段树优化建图单点连区间区间连区间例题解题:倍增优化建图例题解题:前言众所周知,连边的时间复杂度一般是\(O(1)\),但,当连边的对象是一个连续的树上区间的时候,我们或许有更优的连边方式:优化建图。......
  • CF1762E Tree Sum 题解
    题意对于一棵\(n\)个节点的树\(T\),定义\(\operatorname{good}(T)\)为真当且仅当边权\(w\in\left\{-1,1\right\}\)且对于任意节点\(u\),均有\(\displaystylef(u)=\prod\limits_{\left(u,v\right)\inE}w\left(u,v\right)=-1\)。求\[\sum\limits_{\operat......
  • openGauss学习笔记-46 openGauss 高级数据管理-子查询
    openGauss学习笔记-46openGauss高级数据管理-子查询子查询或称为内部查询,嵌套查询,指的是在数据库查询的WHERE子句中嵌入查询语句,相当于临时表。一个SELECT语句的查询结果能够作为另一个语句的输入值。子查询可以与SELECT,INSERT,UPDATE和DELETE语句一起使用。以下是子查询必须遵......