• [Rust学习:二]变量和传参


    一、前言

    • 在本文通过写一题实战,初步探索了这些技能:
      1. Cargo.toml引入库的配置
      2. main中使用外部库:proconio、itertools、superslice。
      3. 函数声明的形参实参写法以及注意点。
      4. 如何方便的读入(proconio::input!)。
      5. 如何方便的数组join(引入itertools::Itertools后ans.iter().join(“\n”))。
      6. 有序数组的二分查找(引入superslice::Ext后dp.lower_bound(&x))。
      7. 基础整型使用(优先usize吧)。
    • Rust的变量、传参、权限控制真的反人类。
    • 碎碎念:
      1. 实现时通常会使用数组下标作为特征,如:a是点权数组,点化作0~n-1下标来索引点权,这就导致了点的类型必须是usize。那么负值
        就不能用,导致dfs初始fa参数反习惯,很难受。
      2. 后来复盘代码发现很多行尾忘记写分号,也过了,观察了一下发现基本都是代码块的末行,如大括号{}内最后一行。
        这是因为末行如果不写分号默认会帮你把这个表达式作为return的值。

    在这里插入图片描述

    二、练习

    1. 前置:a:&T、a:&mut T、mut a:&T、mut a:&mut T的区别

    • a:&T:引用不可变,且引用指向的内容不可修改。
    • a:&mut T:引用不可变,引用指向的内容可修改。
    • mut a:&T:引用可变(a可重新赋值),但引用指向的内容不可修改。
    • mut a:&mut T:引用可变,引用指向的内容可修改。

    2. 例题: F - LIS on Tree

    F - LIS on Tree

    https://atcoder.jp/contests/abc165/tasks/abc165_f
    
    输入 n (2≤n≤2e5) 和长为 n 的数组 a (1≤a[i]≤1e9),表示每个节点的点权。
    然后输入一棵树的 n-1 条边(节点编号从 1 开始)。
    输出 n 个数,第 i 个数为从节点 1 到节点 i 的路径上点权的 LIS 长度。
    
    注:LIS 指最长上升子序列。
    输入
    10
    1 2 5 3 4 6 7 3 2 4
    1 2
    2 3
    3 4
    4 5
    3 6
    6 7
    1 8
    8 9
    9 10
    输出
    1
    2
    3
    3
    4
    4
    5
    2
    2
    3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    这是一道atc上的题,题意比较清楚,写个DFS在路径上跑LIS即可。

    3. 题解。

    • 我们套用LIS的二分优化模板,需要一个单调递增的dp数组:
      let mut dp: Vec = vec![0; 0];
      注意这里需要声明dp是mut,后边的vec!宏可以直接创建一个内容是全是0且长度为0的vector。
    • 建图时,本题无边权有点权,因此可以两层int的vector即可:
      let mut g = vec![Vec::new(); n]; 这里省略了g的类型声明。
    • 读入时,使用input!宏,这个宏在proconio包中,注意加入Cargo.toml。
      Cargo.toml:
      [dependencies]
      proconio = "0.4.2"
      
      • 1
      • 2
      main.rs:
      use proconio::input;
      input! {
          n:usize,
          a:[usize;n],
          es:[(usize,usize);n-1]
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 输出时,由于要输出n行答案,实测用回车拼完一起输出会快很多。这里需要用到use itertools::Itertools;这个库,注意加Cargo.toml:itertools = "0.10.0"
      main.rs: println!("{}", ans.iter().join("\n"));
    • 注意,vector原来不支持二分,需要引入一个库:use superslice::Ext;,Cargo.toml:superslice = "1.0.0";, main.rs:p = dp.lower_bound(&x);

    • 准备工作做好了接下来是最烦的dfs函数编写。
    • rust的fn支持写在main外边或里边,目前没看出什么区别。即使写在里边也用不了main里的变量,需要显式传参。
    • 为了运行速度,显然所有非基础类型(比如集合等)都要传引用&。坑就在这些地方。
    • 来看函数头:
       fn dfs(
          u: usize,  // 当前节点
          fa: usize,  // 父节点
          g: &Vec<Vec<usize>>,  // 图
          dp: &mut Vec<usize>,  // dp
          ans: &mut Vec<usize>,  // 答案
          a: &Vec<usize>, // 点权
      ) {}
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 其中u和fa是基础类型,函数体内也不会变,直接传值即可。这里fa我在其他语言里一般初始传-1,但是这里必须是usize,因此要传入一个不会存在的节点。由于节点被我转化成0~n-1,因此初始传n以上即可。
      • g是不会变的图,用来dfs路径,因此形参直接声明引用。
      • dp是会变的,大小变push、pop和内容修改。因此形参要声明为&mut Vec
      • ans同dp。
      • a是用来查询点权,又不变,声明引用即可。
    • 当main里进dfs时,所有实参要显式的传递引用即:
      main:
      dfs(0, 2000000, &g, &mut dp, &mut ans, &a);
      
      • 1
    • 但在dfs内部自我调用时,由于这些参数本身传进来已经是引用了,因此不加&标志,即:
      dfs:
      dfs(v, u, g, dp, ans, a);
      
      • 1

    三、 代码实现

    • 实现时,由于dfs需要回溯,我使用x变量来储存修改的dp[p]的值,这里可以使用std::mem::swap(&mut x,&mut dp[p]);这个方法,它不会初始化双方的绑定。
    /***
    https://atcoder.jp/contests/abc165/tasks/abc165_f
    
    输入 n (2≤n≤2e5) 和长为 n 的数组 a (1≤a[i]≤1e9),表示每个节点的点权。
    然后输入一棵树的 n-1 条边(节点编号从 1 开始)。
    输出 n 个数,第 i 个数为从节点 1 到节点 i 的路径上点权的 LIS 长度。
    
    注:LIS 指最长上升子序列。
    输入
    10
    1 2 5 3 4 6 7 3 2 4
    1 2
    2 3
    3 4
    4 5
    3 6
    6 7
    1 8
    8 9
    9 10
    输出
    1
    2
    3
    3
    4
    4
    5
    2
    2
    3
     */
    use proconio::input;
    // use std::collections::VecDeque;
    //
    use itertools::Itertools;
    // use petgraph::{Graph, data::Build};
    use superslice::Ext;
    const MOD:usize = 1000000000+7;
    const INF:usize = 1<<60;
    fn main() {
        input!{
            n:usize,
            a:[usize;n],
            es:[(usize,usize);n-1]
        }
        let mut g = vec![Vec::new();n];
        for (u,v) in es {
            g[u-1].push(v-1);
            g[v-1].push(u-1);
        }
        let mut dp:Vec<usize> = vec![0;0];
        // let mut dp:Vec = vec![INF;n];
    
        fn dfs(
            u:usize,
            fa:usize,
            g:&Vec<Vec<usize> >,
            dp: &mut Vec<usize>,
            ans:&mut Vec<usize>,
            a: &Vec<usize>
        ){
            let mut x = a[u];
            let  p ;
            if dp.len() == 0 || x>dp[dp.len()-1] {
                dp.push(x);
                p = a.len();
            }
            else {
                p =  dp.lower_bound(&x);
                // let t = x;
                // x = dp[p];
                // dp[p] = t;
                std::mem::swap(&mut x,&mut dp[p]);
            }
            // ans[u] = dp.lower_bound(&INF);
            ans[u] = dp.len();;
            for &v in g[u].iter(){
                if v != fa{
                    dfs(v,u,g,dp,ans,a);
                }
            }
            if p == a.len() {
                dp.pop();
            }
            else {
                dp[p] = x;
            }
        }
    
        let mut ans = vec![1usize;n];
        dfs(0,2000000,&g,&mut dp,&mut ans,&a);
        // for v in ans {
        //     println!("{}",v)
        // }
        println!("{}", ans.iter().join("\n"));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    六、参考链接

    • https://atcoder.jp/contests/abc165/submissions/36201793
  • 相关阅读:
    activiti流程变量
    SpringCloud——Hystrix(手写断路器思路、测试与优化)
    为什么说 java中的Synchronized是非公平锁
    中国电竞20年:从小众娱乐到新兴体育产业
    第二章 第二十五节:文件操作:with和复制
    手把手教你编写性能测试用例
    天才制造者:独行侠、科技巨头和AI|深度学习崛起十年
    Splunk 之 filed 恢复
    unity鼠标双击
    [2023.09.12]: Yew应用开发的第一个hook--use_state
  • 原文地址:https://blog.csdn.net/liuliangcan/article/details/127702250