🏆 周赛

# 426

@2024.12.01

全国排名得分用时同分首位竞赛分数
412/2447 16.9%18✨1:53:42 (+25)1 12:501647 (+81)

T1. 仅含置位位的最小整数

n 的各二进制位置 1,即 (1 << m) - 1,其中 mn 的二进制位数。

impl Solution {
    pub fn smallest_number(n: i32) -> i32 {
        (1 << (32 - n.leading_zeros())) - 1
    }
}

T2. 识别数组中的最大异常值

从大到小枚举异常值,检查剩余元素能否分为 sum() 相同的两组,且其中一组只有一个元素。

use std::collections::HashMap;
 
impl Solution {
    pub fn get_largest_outlier(mut nums: Vec<i32>) -> i32 {
        // 检查: 移除 banned 后, 是否存在一个元素 A,
        // 使得剩余元素(移除 banned 和 A)之和为 A
        fn check(sum: i32, banned: i32, count: &HashMap<i32, usize>) -> bool {
            let sum = sum - banned;
            if sum % 2 != 0 {
                return false;
            }
 
            let tar = sum / 2;
            *count.get(&tar).unwrap_or(&0) >= if tar == banned { 2 } else { 1 }
        }
 
        // 倒序排序
        nums.sort_unstable_by(|a, b| b.cmp(a));
 
        // 求和
        let sum: i32 = nums.iter().sum();
 
        // 计数
        let mut count = HashMap::new();
        for &x in &nums {
            let e = count.entry(x).or_insert(0);
            *e += 1;
        }
 
        // 枚举异常值
        for &x in &nums {
            if check(sum, x, &count) {
                return x;
            }
        }
 
        unreachable!()
    }
}

T3. 连接两棵树后最大目标节点数目 I

对图 1 的每个结点,BFS 计算其在图 1 中 k 步可达结点数量,加上图 2 中 k-1 步可达的最大结点数量(事先对图 2 的每个起点计算一次,取最大)即为 ans[i]

use std::collections::VecDeque;
 
impl Solution {
    pub fn max_target_nodes(edges1: Vec<Vec<i32>>, edges2: Vec<Vec<i32>>, k: i32) -> Vec<i32> {
        /// 构造邻接表
        fn next(edges: &[Vec<i32>], n: usize) -> Vec<Vec<i32>> {
            let mut next = vec![vec![]; n];
 
            for e in edges {
                let (x, y) = (e[0], e[1]);
                next[x as usize].push(y);
                next[y as usize].push(x);
            }
 
            next
        }
 
        /// BFS 搜索从 start 开始, k 步可达的结点数量,
        /// start 本身也算一个
        fn bfs_k_reachable(next: &[Vec<i32>], start: usize, k: i32) -> usize {
            let mut vis = vec![false; next.len()];
            let mut q = VecDeque::new();
            let mut reach = 0;
 
            vis[start] = true;
            q.push_back(start);
 
            for _ in 0..=k {
                if q.is_empty() {
                    return reach;
                }
 
                let cnt = q.len();
                reach += cnt;
 
                for _ in 0..cnt {
                    let x = q.pop_front().unwrap();
                    for y in &next[x] {
                        let y = *y as usize;
                        if !vis[y] {
                            vis[y] = true;
                            q.push_back(y);
                        }
                    }
                }
            }
 
            reach
        }
 
        // 邻接表
        let next1 = next(&edges1, edges1.len() + 1);
        let next2 = next(&edges2, edges2.len() + 1);
 
        // 计算图 2 中 k-1 步可达的最大结点数量(只计算 1 次)
        let reach2_max = (0..next2.len())
            .map(|i| bfs_k_reachable(&next2, i, k - 1))
            .max()
            .unwrap_or(1);
 
        let mut ans = vec![];
 
        // 对图 1 的每个结点, 计算其在图 1 中 k 步可达结点数量,
        // 加上已计算的图 2 的最大值
        for i in 0..next1.len() {
            let a = bfs_k_reachable(&next1, i, k) + reach2_max;
            ans.push(a as i32);
        }
 
        ans
    }
}

T4. 连接两棵树后最大目标节点数目 II

定义距离为偶数的两个结点是“可达”的。

观察发现:每个图都可以分为两组,每组内的任意两结点“可达”,两组间的任何两结点“不可达”。

BFS 对两图各遍历一次,标记每个结点所属分组。

因为总可以控制图 1 和图 2 的连接结点,使得对于任意的 i,总能让 i 与图 2 中较大的那一组结点“可达”。

每个 ans[i] 即为:图 1 中与 i 属于同一分组的结点数量,加上图 2 中较大分组的结点数量。

use std::collections::{HashSet, VecDeque};
 
impl Solution {
    pub fn max_target_nodes(edges1: Vec<Vec<i32>>, edges2: Vec<Vec<i32>>) -> Vec<i32> {
        /// 构造邻接表
        fn next(edges: &[Vec<i32>], n: usize) -> Vec<Vec<i32>> {
            let mut next = vec![vec![]; n];
 
            for e in edges {
                let (x, y) = (e[0], e[1]);
                next[x as usize].push(y);
                next[y as usize].push(x);
            }
 
            next
        }
 
        /// BFS 将结点分为两组, 每组内的任意结点间距离均为偶数,
        /// 返回两个 set 分别包含每组的结点编号,
        /// start 结点代表的组在第一个 set 中
        fn bfs_bisect(next: &[Vec<i32>], start: usize) -> (HashSet<usize>, HashSet<usize>) {
            let mut vis = vec![false; next.len()];
            let mut q = VecDeque::new();
            let mut even = false;
            let mut g = (HashSet::new(), HashSet::new());
 
            vis[start] = true;
            q.push_back(start);
            g.0.insert(start);
 
            loop {
                if q.is_empty() {
                    return g;
                }
 
                let cnt = q.len();
 
                for _ in 0..cnt {
                    let x = q.pop_front().unwrap();
                    for y in &next[x] {
                        let y = *y as usize;
                        if !vis[y] {
                            vis[y] = true;
                            if even {
                                g.0.insert(y);
                            } else {
                                g.1.insert(y);
                            }
                            q.push_back(y);
                        }
                    }
                }
 
                even = !even;
            }
        }
 
        // 邻接表
        let next1 = next(&edges1, edges1.len() + 1);
        let next2 = next(&edges2, edges2.len() + 1);
 
        // 将图 2 一分为二
        let (g2_even, g2_odd) = bfs_bisect(&next2, 0);
        // 取较大组的结点数量,
        // 因为总可以控制图 2 的连接结点,
        // 使得图 1 的任意结点到本较大组的结点的距离为偶数
        let c2_max = g2_even.len().max(g2_odd.len());
 
        let mut ans = vec![];
 
        // 将图 1 一分为二
        let g1 = bfs_bisect(&next1, 0);
        // 对图 1 的每个结点, 计算其在图 1 中距离为偶数的结点数量,
        // 即其所属分组的结点数量, 加上已计算的图 2 的最大值
        for i in 0..next1.len() {
            let a = if g1.0.contains(&i) {
                g1.0.len()
            } else {
                g1.1.len()
            } + c2_max;
            ans.push(a as i32);
        }
 
        ans
    }
}

# 427

@2024.12.08

全国排名得分用时同分首位竞赛分数
279/2376 11.8%1248:31 (+5)85 12:121736 (+89)

T1. 转换数组

直接写。

impl Solution {
    pub fn construct_transformed_array(nums: Vec<i32>) -> Vec<i32> {
        let n = nums.len();
 
        nums.iter()
            .enumerate()
            .map(|(i, &x)| {
                if x > 0 {
                    nums[(i + x as usize) % n]
                } else {
                    nums[(i + n - ((-x as usize) % n)) % n]
                }
            })
            .collect()
    }
}

T2. 用点构造面积最大的矩形 I

枚举所有点对,试作为对角顶点,检查是否符合要求。

impl Solution {
    pub fn max_rectangle_area(points: Vec<Vec<i32>>) -> i32 {
        fn check(p: &[Vec<i32>], i: usize, j: usize) -> i32 {
            let (x1, y1) = (p[i][0], p[i][1]);
            let (x2, y2) = (p[j][0], p[j][1]);
            if x1 == x2 || y1 == y2 || !p.contains(&vec![x1, y2]) || !p.contains(&vec![x2, y1]) {
                return -1;
            }
 
            let max_x = x1.max(x2);
            let min_x = x1.min(x2);
            let max_y = y1.max(y2);
            let min_y = y1.min(y2);
 
            let inside = |x: i32, y: i32| -> bool {
                !((x == x2 || x == x1) && (y == y2 || y == y1))
                    && (min_x..=max_x).contains(&x)
                    && (min_y..=max_y).contains(&y)
            };
 
            for c in p {
                if inside(c[0], c[1]) {
                    return -1;
                }
            }
 
            (max_x - min_x) * (max_y - min_y)
        }
 
        let n = points.len();
        let mut ans = -1;
 
        for i in 0..n {
            for j in i..n {
                ans = ans.max(check(&points, i, j));
            }
        }
 
        ans
    }
}

T3. 长度可被 K 整除的子数组的最大元素和

k 个一组求和得 sums,枚举 0..k 作为起点,从每个起点开始以 k 为步长,将 sums 的子序列视为新数组,对该数组求最大非空子数组和,所有和取最大者。

impl Solution {
    pub fn max_subarray_sum(nums: Vec<i32>, k: i32) -> i64 {
        /// 对 nums 每 k 个一组求和,
        /// 如 nums=[-5,1,2,-3,4], k=2,
        /// 返回 [-4,3,-1,1]
        fn sums(nums: &[i64], k: usize) -> Vec<i64> {
            let mut p = nums.iter().take(k - 1).sum();
            let mut ret = vec![];
 
            for i in k - 1..nums.len() {
                p += nums[i];
                ret.push(p);
                p -= nums[i - k + 1];
            }
 
            ret
        }
 
        /// 从 start 开始, 步长为 k 视为一个新数组,
        /// 返回新数组的最大非空子数组和,
        /// 如 part=[-4,3,-1,1], start=1, k=2,
        /// 新数组为 [3,1], 返回 4
        fn max_sub(part: &[i64], start: usize, k: usize) -> i64 {
            let mut cur = 0;
            let mut ret = i64::MIN;
 
            for p in part.iter().skip(start).step_by(k) {
                cur += *p;
                ret = ret.max(cur);
                if cur < 0 {
                    cur = 0;
                }
            }
 
            ret
        }
 
        // 类型转换
        let k = k as usize;
        let nums: Vec<i64> = nums.iter().map(|&x| x as i64).collect();
 
        // 每 k 个元素一组求和
        let sums = sums(&nums, k);
 
        // 0..k 分别作为起点, 步长为 k,
        // 求该组元素的最大非空子数组和
        (0..k).map(|i| max_sub(&sums, i, k)).max().unwrap_or(-1)
    }
}

T4. 用点构造面积最大的矩形 II

impl Solution {
    pub fn max_rectangle_area(x_coord: Vec<i32>, y_coord: Vec<i32>) -> i64 {
        unimplemented!()
    }
}

# 429

全国排名得分用时同分首位竞赛分数
a/b c.d%21✨1:43:31 (+40)1 8:33x (+)

T1. 使数组元素互不相同所需的最少操作次数

倒序遍历。

impl Solution {
    pub fn minimum_operations(nums: Vec<i32>) -> i32 {
        let mut vis = [false; 101];
 
        for (i, x) in nums.into_iter().enumerate().rev() {
            let x = x as usize;
            if vis[x] {
                return (i + 1).div_ceil(3) as i32;
            }
            vis[x] = true;
        }
 
        0
    }
}

T2. 执行操作后不同元素的最大数量

impl Solution {
    pub fn max_distinct_elements(mut nums: Vec<i32>, k: i32) -> i32 {
        nums.sort_unstable();
 
        // 同值计数
        // 例如 [1,2,2,3,3,4]
        // 变为 [(1,1),(2,2),(3,2),(4,1)]
        let mut v = nums.into_iter().map(|x| (x, 1)).collect::<Vec<_>>();
        v.dedup_by(|a, b| {
            if a.0 == b.0 {
                b.1 += a.1;
                true
            } else {
                false
            }
        });
 
        if k == 0 {
            return v.len() as i32;
        }
 
        // p: 上一个 x 占用的最大值
        let mut p = v[0].0 - k - 1;
        let mut ans = 0;
        for (x, cnt) in v {
            // 修正 p, 不小于本 x 可以占用的最小值
            p = p.max(x - k - 1);
            // 新的 p 为 min(上个 p + 本组计数, 本 x + k),
            // 即 (贪心上限, 题意上限)
            let new_p = (p + cnt).min(x + k);
            // 两 p 插值即为本 x 实际占用的数量
            ans += new_p - p;
            p = new_p;
        }
 
        ans
    }
}
impl Solution {
    pub fn max_distinct_elements(mut nums: Vec<i32>, k: i32) -> i32 {
        nums.sort_unstable();
 
        let mut exp = i32::MIN;
        let mut ans = 0;
        for x in nums {
            let (min, max) = (x - k, x + k);
 
            if exp <= min {
                exp = min + 1;
                ans += 1;
            } else if exp <= max {
                exp += 1;
                ans += 1;
            }
        }
 
        ans
    }
}

T3 & T4. 字符相同的最短子字符串 I & II

impl Solution {
    pub fn min_length(s: String, num_ops: i32) -> i32 {
        fn flip(c: char) -> char {
            if c == '0' {
                '1'
            } else {
                '0'
            }
        }
 
        fn check(mut chs: Vec<char>, mut ops: i32, m: usize) -> bool {
            let n = chs.len();
            let (mut l, mut r) = (0, 0);
 
            while r < n {
                let c = chs[l];
                while r < n && chs[r] == c && r - l < m {
                    r += 1;
                }
 
                if r < n && r - l == m && chs[r] == c {
                    if ops == 0 {
                        return false;
                    }
                    ops -= 1;
 
                    if m >= 2 {
                        r += 1;
                    } else {
                        chs[r] = flip(chs[r]);
                    }
                }
 
                l = r;
            }
 
            true
        }
 
        let n = s.len();
        let (mut l, mut r) = (1, s.len() as i32);
 
        let mut pre: Vec<char> = s.chars().collect();
        pre[0] = flip(pre[0]);
 
        let mut suf: Vec<char> = s.chars().collect();
        suf[n - 1] = flip(suf[n - 1]);
 
        let s: Vec<char> = s.chars().collect();
 
        while l < r {
            let m = l + ((r - l) >> 1);
            let v = if num_ops == 0 {
                check(s.clone(), num_ops, m as usize)
            } else {
                check(s.clone(), num_ops, m as usize)
                    || check(pre.clone(), num_ops - 1, m as usize)
                    || check(suf.clone(), num_ops - 1, m as usize)
            };
 
            if v {
                r = m;
            } else {
                l = m + 1;
            }
        }
 
        l
    }
}