DFS序专题

2023-05-16

牛客专题之DFS序

简介

dfs序:每个节点在dfs深度优先遍历中的进出栈的时间序列,也就是tarjan算法中的dfn数组。

画个图理解一下:
在这里插入图片描述

这棵树的dfs序:1 3 2 4 2 5 6 7 6 5 1

那么这个序列有什么用呢?

通过观察,两个相同数字之间就是以它为根的子树, 也就是说,通过dfs序我们可以得到,这个节点第一次进入栈的时间戳 l i l_i li和第一次出栈的时间戳 r i r_i ri。之后我们就可以通过 l i l_i li r i r_i ri操纵这棵树了。

具体看题:

模板

Military Problem 原CF1006E

题意

你有一棵有 n n n个节点的树,有 q q q次询问,每次询问有 ( u , k ) (u,k) (u,k),指从以 u u u为根的子树出发先序遍历到达的第 k k k个点是哪一个?如果不存在,输出 − 1 -1 1

一道模板题。先预处理出每个点的dfs序, 即每个点的 l i l_i li r i r_i ri
询问的时候判断是否 l [ u ] + k − 1 > r [ u ] l[u] + k - 1 > r[u] l[u]+k1>r[u], 否则输出 a [ l [ u ] + k − 1 ] a[l[u]+k-1] a[l[u]+k1]就好了。

/*
https://ac.nowcoder.com/acm/problem/112932
https://codeforces.com/problemset/problem/1006/E
*/

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int a[maxn], cnt = 0;
int l[maxn], r[maxn];
vector<int> G[maxn];

void dfs(int u, int fa){
    l[u] = ++cnt;
    a[cnt] = u;
    for(auto v : G[u]) {
        if(fa == v) continue;
        dfs(v, u);
    }
    r[u] = cnt;
}

int main(){
    int n, q;
    cin >> n >> q;
    for(int i = 2; i <= n; i++) {
        int u; cin >> u;
        G[i].push_back(u);
        G[u].push_back(i);
    }
    dfs(1, 0);
    for(int i = 1; i <= q; i++){
        int u, k;
        cin >> u >> k;
        if(l[u] + k - 1 > r[u]) cout << -1 << endl;
        else cout << a[l[u] + k - 1] << endl;
    }
    return 0;
}

选点

题意

有一棵 n n n个节点的二叉树,每个节点有权值 w i w_i wi,要选尽量多的点,但是得满足以下限制:
对于任意一棵子树,都要满足:

  • 如果选了根节点的话,在这棵子树内选的其他的点都要比根节点的值大
  • 如果在左子树选了一个点,在右子树中选的其他点要比它小。
思路

读题后可以得到:根的值< 右子树的最大值 < 左子树的最大值, 如果要得到尽量多的点,只需要记录从根 -> 右子树 -> 左子树的dfs序(此处为入栈时间戳 l i l_i li), 在 l i l_i li上找最长上升子序列(没想到。。)即可。

代码
/*
https://ac.nowcoder.com/acm/problem/22494
*/

#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int a[maxn], tot = 0, cnt;
int l[maxn], r[maxn], w[maxn], ans[maxn];
vector<int> G[maxn];

void dfs(int u){
    if(u == 0) return;
    a[++tot] = w[u];
    dfs(r[u]); dfs(l[u]);
}

int main(){
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> w[i]; 
    for(int i = 1; i <= n; i++) cin >> l[i] >> r[i];

    dfs(1);
    ans[++cnt] = a[1];
    // 最长上升子序列贪心O(nlogn)解法
    for(int i = 2; i <= n; i++){
        if(a[i] > ans[cnt]) ans[++cnt] = a[i];
        else{
            int t = lower_bound(ans + 1, ans + 1 + cnt, a[i]) - ans;
            ans[t] = a[i];
        }
    }
    cout << cnt << endl;
    return 0;
}

dfs序+树状数组/线段树

求和

题意

一颗以 k k k为根有 n n n 个节点的树,每个节点有一个点权 v i v_i vi。有 m m m 次操作

  • 1 x y 表示将节点 x x x的权值加上 y y y
  • 2 x 表示求以 x x x为根的子树上所有节点(包括 x x x)的和
思路:

通过 dfs 序将一整棵子树上映射到序列中连续的一段上。问题就变成对数组进行:单点修改,区间查询。用树状数组维护就好了。

代码
/*
https://ac.nowcoder.com/acm/problem/204871
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define pb push_back

const int maxn = 1e6 + 10;

int l[maxn], r[maxn];
int bits[maxn];
vector<int> G[maxn];
int n, m, k, cnt = 0;


int lowbit(int x){
    return x & (-x);
}
void add(int x, int val){
    while(x < maxn){
        bits[x] += val;
        x += lowbit(x);
    }
}
int query(int x){
    int res = 0;
    while(x){
        res += bits[x];
        x -= lowbit(x);
    }
    return res;
}

void dfs(int u, int fa){
    l[u] = ++cnt;
    for(auto v : G[u]){
        if(v == fa) continue;
        dfs(v, u);
    }
    r[u] = cnt;
}

int a[maxn], op[maxn], b[maxn], val[maxn];

int main(){
    cin >> n >> m >> k;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i < n; i++){
        int u, v; cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(k, 0);
    for(int i = 1; i <= n; i++) add(l[i], a[i]);
    for(int i = 0; i < m; i++){
        int op; cin >> op;
        if(op == 1){
            int x, y; cin >> x >> y;
            add(l[x], y);
        }
        else{
            int x; cin >> x;
            cout << (query(r[x]) - query(l[x]-1)) << endl;
        }
    }
    return 0;
}

Propagating tree 原CF383C

题意

一颗以 1 1 1为根有 n n n 个节点的树,每个节点有一个点权 a i a_i ai。有 m m m 次操作

  • 1 x y 表示将 x x x 结点权值 + v a l +val +val x x x 的儿子权值 − v a l -val val x x x 的孙子们 + v a l +val +val, 以此类推。
  • 2 x 表示 x x x的点权
思路

乍一看是树状数组差分,区间修改,单点查询。但是第一个操作搞不定,仔细观察后得到 1 1 1操作和每个节点的深度奇偶有关。所以只需判断一下深度,决定修改的值的正负就可以(又没想到。。。)。

代码
/*
https://ac.nowcoder.com/acm/problem/110318
*/
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define pb push_back

const int maxn = 1e6 + 10;

int l[maxn], r[maxn];
int bits[maxn];
vector<int> G[maxn];
int n, m, k, cnt = 0;
int a[maxn], dep[maxn];

int lowbit(int x){
    return x & (-x);
}
void add(int x, int val){
    while(x < maxn){
        bits[x] += val;
        x += lowbit(x);
    }
}
int query(int x){
    int res = 0;
    while(x){
        res += bits[x];
        x -= lowbit(x);
    }
    return res;
}

void dfs(int u, int fa){
    l[u] = ++cnt;
    dep[u] = dep[fa] + 1;
    for(auto v : G[u]){
        if(v == fa) continue;
        dfs(v, u);
    }
    r[u] = cnt;
}


int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i < n; i++){
        int u, v; cin >> u >> v;
        G[u].pb(v);
        G[v].pb(u);
    }
    
    dfs(1, 0);
    // for(int i = 1; i <= n; i++) add(l[i], a[i]);
    for(int i = 0; i < m; i++){
        int op; cin >> op;
        if(op == 1){
            int x, y; cin >> x >> y;
            if(dep[x] & 1){
                add(l[x], y);
                add(r[x] + 1, -y);
            } else {
                add(l[x], -y);
                add(r[x] + 1, y);
            }
        }
        else{
            int x; cin >> x;
            int y = query(l[x]);
            if(dep[x] & 1) cout << a[x] + y << endl;
            else cout << a[x] - y << endl;
        }
    }
    return 0;
}

[华华和月月种树]https://ac.nowcoder.com/acm/problem/23051)

题意

华华和月月一起维护了一棵动态有根树,每个点有一个权值。刚开存档的时候,树上只有 0 号节点,权值为 0 。接下来三种操作:

  • 1 1 1 i i i 表示月月氪金使节点 i i i 长出了一个新的儿子节点,权值为 0 0 0,编号为当前最大编号 + 1 +1 +1(也可以理解为,当前是第几个操作 1 1 1,新节点的编号就是多少)。
  • 2 2 2 i i i a a a 表示华华上线做任务使节点 i i i 的子树中所有节点(即它和它的所有子孙节点)权值加 a a a
  • 3 3 3 i i i,华华需要给出 i i i 节点此时的权值。
思路

树状数组区间更新单点求值。

操作 1 1 1可以将新加的点的减去它的父亲的权值来使得新加的点值为 0 0 0

代码
/*
https://ac.nowcoder.com/acm/problem/23051

https://blog.nowcoder.net/n/0055f19c0e49422786d7b7981a914709
*/

#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define pb push_back

const int maxn = 1e6 + 10;

int l[maxn], r[maxn];
int bits[maxn];
vector<int> G[maxn];
int m, cnt = 0;

int lowbit(int x){
    return x & (-x);
}
void add(int x, int val){
    while(x <= cnt + 1){
        bits[x] += val;
        x += lowbit(x);
    }
}
int query(int x){
    int res = 0;
    while(x){
        res += bits[x];
        x -= lowbit(x);
    }
    return res;
}

void dfs(int u){
    l[u] = ++cnt;
    for(auto v : G[u]) dfs(v);
    r[u] = cnt;
}

int a[maxn], op[maxn], b[maxn], val[maxn];

int main(){
    cin >> m;
    for(int i = 0; i < m; i++){
        cin >> op[i] >> a[i];
        if(op[i] == 2) cin >> b[i];
        else if(op[i] == 1){
            G[a[i]].pb(++cnt);
            b[i] = cnt;
        }
    }
    dfs(0);
    // cout << cnt << endl;
    for(int i = 0; i < m; i++){
        if(op[i] == 1) val[l[b[i]]] -= query(l[a[i]]); 
        else if(op[i] == 2){
            add(l[a[i]], b[i]);
            add(r[a[i]] + 1, -b[i]);
        }
        else cout << val[l[a[i]]] + query(l[a[i]]) << endl; 
    }
    return 0;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DFS序专题 的相关文章

  • LZW压缩算法(数据无损压缩)

    目录 一 LZW算法介绍 二 算法介绍 1 LZ xff37 算法的基本概念 2 LZW压缩的基本原理 3 LZW算法流程 xff1a 零 常用无损数据压缩算法 字典算法 游程编码 基于字典编码技术的LZW算法 基于哈夫曼编码原理的压缩算法
  • sftp账号创建和权限设置

    操作前需先开启telnet服务 xff0c 防止修改sshd config后 xff0c sshd服务启不了 systemctl span class token keyword start span telnet span class t
  • Python【列表】

    文章目录 1 列表的方法及注释2 其他修改列表的办法2 列表推导式3 列表的切片4 列表转换4 1 字符串转列表 xff1a 4 2 列表转字符串 list 列表 是一个可变序列 1 列表的方法及注释 列表的方法注释append x 将元素
  • FTP的port模式和pasv模式

    FTP的port模式和pasv模式 FTP具有两种模式 xff0c 分别是port模式 也叫主动模式 和pasv模式 也叫被动模式 主动模式 主动模式的FTP是指服务器主动连接客户端的数据端口 xff0c 可以理解为服务端主动给客户端传输文
  • shell for循环多个变量

    1 使用花括号 var1 var2 var3 a 61 span class token string 34 apple 34 span span class token punctuation span b 61 span class t
  • shell 基本运算符

    文章目录 1 算数运算2 关系运算符3 布尔运算符4 逻辑运算符5 字符串运算符6 文件测试运算符知识点 1 算数运算 方法一 sum1 61 96 expr 3 span class token operator 43 span 5 96
  • Dockerfile简介

    1 什么是dockerfile Dockerfile是一个包含用于组合映像的命令的文本文档 可以使用在命令行中调用任何命令 Docker通过读取Dockerfile中的指令自动生成映像 docker build命令用于从Dockerfile
  • 容器通信之跨链接通信

    前言 同一主机下搭建容器应用栈的环境 xff0c 只需要完成容器互联来实现容器间的通信即可 xff0c 这里采用docker run link选项建立容器间的互联关系 docker官方已不推荐使用docker run link来链接2个容器
  • Linux进程间通信

    1 unix域套接字 域套接字 xff1a 1 只能用于同一设备上不同进程之间的通信 xff1b 2 效率高于网络套接字 域套接字仅仅是复制数据 xff0c 并不走协议栈 xff1b 3 可靠 xff0c 全双工 xff1b 2 IP套接字
  • 什么是API

    1 什么是API API是Application Programming Interface xff08 应用程序接口 xff09 的缩写 是一些预先定义的函数 xff0c 目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力
  • FreeRTOS(二)任务基础知识

    一 前后台系统与RTOS 前后台系统 61 死循环 xff08 通常为1个 xff09 43 中断服务程序 xff08 通常为若干个 xff09 应用程序是一个无限循环 xff0c 循环中调用 API 函数完成所需的操作 xff0c 这个大
  • SBUS协议(20200210)

    最近看到很多sbus协议 xff0c 就专门搜集了一些资料学习一下 1 介绍 SBUS是一个接收机串行总线输出 xff0c 通过这根总线 xff0c 可以获得遥控器上所有通道的数据 目前很多模型及无人机电子设备都支持SBUS总线的接入 使用
  • 【openmv专题】串口通信

    这篇文章主要讲述openmv串口通信过程中会出现错位 xff0c 因缓存空间不足带来的串口报错问题 xff0c 直接进入正题 xff1a 串口通信有同步和异步之分 xff0c 而openmv用的是异步通信 xff0c 需要有缓存区 xff0
  • FreeRTOS任务上下文切换与任务状态切换的区别及联系

    FreeRTOS 中的任务上下文切换和任务状态切换是两个不同的概念 1 任务状态切换是指任务从一种状态切换到另一种状态 FreeRTOS 中的任务状态包括就绪态 阻塞态和运行态 当任务从就绪态切换到运行态时 xff0c 任务开始执行 xff
  • XGBOD:用无监督表示学习改进有监督离群点检测

    XGBOD Improving Supervised Outlier Detection with Unsupervised Representation Learning 论文链接 xff1a https www andrew cmu e
  • 小觅S系列相机运行vins-mono(轨迹飘飞解决版)

    小觅S系列相机运行vins mono xff08 轨迹飘飞解决版 xff09 1 SDK驱动2 获得相机标定数据3 下载MYNT EYE VINS Sample4 运行 前期准备 xff1a 安装并成功运行VINS MONO 1 SDK驱动
  • 嵌入式第0部分:嵌入式工程师完全学习指南

    一 什么是嵌入式 xff08 一 xff09 定义 xff1a 传统定义 xff08 狭义嵌入式 xff09 xff1a 嵌入式系统是以应用为中心 xff0c 以计算机技术为基础 xff0c 并且软硬件课裁剪 xff0c 适用于应用系统对功
  • 【SLAM 十四讲】---第七讲、视觉里程计

    第七讲 视觉里程计
  • Vscode配置git

    1 Git介绍和安装 Git是什么 Git是目前世界上最先进的分布式版本控制系统 xff08 没有之一 xff09 简单来说 它是控制项目版本的一个工具 我们可以利用Git进行多人协作和代码备份等工作 下载git xff08 64bit w
  • Xshell连接虚拟机Ubantu失败解决办法(主机和虚拟机能够互ping的前提)

    主机和虚拟机互ping 在主机命令行里输入ipconfig指令 xff0c 查询主机ip地址 xff0c 在虚拟机Ubantu终端里输入ping 主机ip地址 xff0c ping通后 xff0c 按ctrl 43 c停止 在虚拟机Uban

随机推荐

  • windows 11系统安装

    安装前注意事项 1 准备8G或8G以上U盘 xff08 32G以内 xff09 2 安装系统前备份好个人需要数据 xff08 制作U盘会格式化U盘 xff0c U盘内的重要文件也要事先备份好 xff09 3 预装office的务必记住自己激
  • docker 权限问题 Got permission denied while trying to connect to the Docker daemon socket at

    一 前言 docker安装完成 xff0c 一般用户没有权限启动docker服务 xff0c 只能通过sudo来通过root用户权限来启动docker xff0c 此时对于一般用户而言 xff0c 需要执行docker ps或者docker
  • Neo4j(七)——创建新数据库(如何在Neo4j中创建新数据库)

    方法一 xff1a 找到neo4j安装目录 xff0c 编辑conf文件夹中的neo4j conf 找到dbms active database 61 xff0c 将下图中的graph db用其他名称替换 xff0c 并解除注释 xff08
  • python VScode使用gitlab简单使用流程

    一 下载安装软件 1 安装好vscode xff0c 如未安装 xff0c 下载并且安装 https code visualstudio com Download 2 安装git windows客户端 https git scm com d
  • keil5工程函数无法跳转到函数定义解决方法

    问题描述 在使用keil查看工程代码时 xff0c 进行函数的跳转 xff0c 跳转不成功并提示以下错误 这是因为在编译工程的时候少勾选了一个选项 xff0c 按下以下方式勾选上然后重新Rebuild一下工程就好了
  • Codeforces D. Prefix-Suffix Palindrome

    Codeforces D Prefix Suffix Palindrome 题解 xff1a 和D1相同 xff0c 区别是找中间的回文串要压缩时间 xff0c 用到了马拉车算法 xff08 算法介绍在下面 xff1a span class
  • codeforces 1326 E.Bombs

    codeforces 1326 E Bombs 题意 xff1a 给定 1 n 1 n 1 n 的排列p q xff0c 将
  • Educational Codeforces Round 84 题解

    Educational Codeforces Round 84 题解 A Sum of Odd Integers 题意 xff1a n n n 是否能表示为 k k k 个不同的正奇
  • codeforces 1332 E - Height All the Same(组合数学、奇偶性)

    codeforces 1332 E Height All the Same 组合数学 奇偶性 题意 xff1a 现在有一个 n m n m n m 的方格 xff0c 第 i
  • codeforces 1330 C.D.题解

    codeforces 1330 C D 题解 Dreamoon Likes Coloring 题意 xff1a 给 n lt 61 100000 n lt 61 100000 n lt 61
  • LeetCode数独问题中Bitset的巧妙用处

    LeetCode数独问题中Bitset的巧妙用处 36 有效的数独 判断一个 9x9 的数独是否有效 只需要根据以下规则 xff0c 验证已经填入的数字是否有效即可 数字 1 9 在每一行只能出现一次 数字 1 9 在每一列只能出现一次 数
  • Morris 遍历

    Morris 遍历 中序遍历 前言 我们在中序遍历的时候 一定先遍历左子树 然后遍历当前节点 最后遍历右子树 在常规方法中 我们用递归回溯或者是栈来保证遍历完左子树可以再回到当前节点 但这需要我们付出额外的空间代价 我们需要用一种巧妙地方法
  • 第九届蓝桥杯c/c++A组省赛题解

    分数 题目 1 1 43 1 2 43 1 4 43 1 8 43 1 16 43 每项是前一项的一半 xff0c 如果一共有20项 求这个和是多少 xff0c 结果用分数表示出来 类似 xff1a 3 2 当然 xff0c 这只是加了前2
  • Ltp介绍及实践(20200925)

    Ltp中源代码和模型包括 xff1a 中文分词 词性标注 未登录词识别 依存句法 语义角色标注几个模块 目录 1 标注集合 分词标注集 词性标注集 命名实体识别标注集 依存句法关系 语义角色类型 2 快速使用 载入模型 分句 用户自定义词典
  • 第十一届蓝桥杯省赛C/C++B组题解

    试题 A 跑步训练 本题总分 xff1a 5 分 题目 问题描述 小明要做一个跑步训练 初始时 xff0c 小明充满体力 xff0c 体力值计为 10000 如果小明跑步 xff0c 每分钟损耗 600 的体力 如果小明休息 xff0c 每
  • codeforces Round680 C. Division 题解

    codeforces Round680 C Division 题解 题目 Oleg s favorite subjects are History and Math and his favorite branch of mathematic
  • Codeforces Round #677 (Div. 3) 题解

    Codeforces Round 677 Div 3 题解 A Boring Apartments 题目 题解 简单签到题 xff0c 直接数 xff0c 小于这个数的 43 10 43 10 43 1 0 代码 span class to
  • Leetcode 327. 区间和的个数 (前缀和 + 离散化 + 树状数组)

    Leetcode 327 区间和的个数 前缀和 43 离散化 43 树状数组 题目 题意 有多少个连续的子数组 xff0c 其和在 l o w e r
  • 01 Trie 专题

    01 Trie 专题 异或最大值 The xor largest pair 题意 xff1a 异或最大值的模板 一个数和一个序列中一个数的异或最大值是多少 xff1f 要支持询问 思路 考虑把序列插入 xff0c 构建一个 Trie tex
  • DFS序专题

    牛客专题之DFS序 简介 dfs序 每个节点在dfs深度优先遍历中的进出栈的时间序列 xff0c 也就是tarjan算法中的dfn数组 画个图理解一下 xff1a 这棵树的dfs序 xff1a 1 3 2 4 2 5 6 7 6 5 1 那