LeetCode --- 154双周赛

题目列表

3512. 使数组和能被 K 整除的最少操作次数
3513. 不同 XOR 三元组的数目 I
3514. 不同 XOR 三元组的数目 II
3515. 带权树中的最短路径

一、使数组能被 k 整除的最小操作次数

本题就是问你数组元素和减去几才能成为 k 的倍数,用 取模运算 即可,代码如下

cpp 复制代码
// C++
class Solution {
public:
    int minOperations(vector<int>& nums, int k) {
        return accumulate(nums.begin(), nums.end(), 0) % k;
    }
};
python 复制代码
# Python
class Solution:
    def minOperations(self, nums: List[int], k: int) -> int:
        return sum(nums) % k

二、不同 XOR 三元组的数目 I

本题,找规律(可以打个表看看)本题和给的数组的元素顺序并没有关系,只和选择哪三个数有关系

  • 当 n = 1 n=1 n=1 时,只有 1,只能选 3个1,异或结果只有 1

  • 当 n = 2 n=2 n=2 时,有 1和2,异或结果可以是 1、2,共 2

  • 当 n ≥ 3 n\ge3 n≥3 时, [ 0 , 2 L ) [0,2^L) [0,2L) 中的任何数都能得到,共有 2 L 2^L 2L 个,其中 L = ⌊ l o g 2 n ⌋ + 1 L= \lfloor log_2n\rfloor+1 L=⌊log2n⌋+1,即 n n n 的二进制长度,解释如下

    • 可以选择 1、2、3 异或为 0

    • 可以选择 3 个相同的数,异或为它们本身,可以构成 [1,n] 中的任意一个数

    • 能组成多少个 ≥ n \ge n ≥n 的数呢?

      • 异或的结果不可能超过 2 L − 1 2^L-1 2L−1
      • 对于 [ n + 1 , 2 L − 1 ] [n+1,2^L-1] [n+1,2L−1] 中的任意一个数 a a a,我们可以将其拆分为 2 L − 1 ⊕ b 2^{L-1}\oplus b 2L−1⊕b,再将 b b b 拆分为 c ⊕ 1 c\oplus 1 c⊕1,即 a = 2 L − 1 ⊕ c ⊕ 1 a=2^{L-1}\oplus c\oplus 1 a=2L−1⊕c⊕1,特殊的,当 a = 2 L − 1 + 1 a=2^{L-1}+1 a=2L−1+1 时,此时 c = 0 c=0 c=0,但是 c ∈ [ 1 , n ] c\in[1,n] c∈[1,n],不能取 0 0 0,可以这样构造 a = 2 L − 1 ⊕ 2 ⊕ 3 a=2^{L-1}\oplus 2\oplus 3 a=2L−1⊕2⊕3

代码如下

cpp 复制代码
// C++
class Solution {
public:
    int uniqueXorTriplets(vector<int>& nums) {
        int n = nums.size();
        if(n <= 2) return n;
        return 1 << bit_width((unsigned)n);
    }
};
python 复制代码
# Python
class Solution:
    def uniqueXorTriplets(self, nums: List[int]) -> int:
        n = len(nums)
        if n <= 2: return n
        return 1 << n.bit_length()

三、不同 XOR 三元组的数目 II

由于 1 <= nums[i] <= 1500,故异或的结果最多只有 2 ⌊ l o g 2 ( 1500 ) ⌋ + 1 = 2048 2^{ \lfloor log_2(1500) \rfloor+1}=2048 2⌊log2(1500)⌋+1=2048,我们可以将暴力枚举的三重循环,变成两个两层循环,来进行模拟,代码如下

cpp 复制代码
// C++
class Solution {
public:
    int uniqueXorTriplets(vector<int>& nums) {
        int n = nums.size();
        int mx = ranges::max(nums);
        int N = 1 << bit_width((unsigned)mx);
        vector<int> hash(N);
        // 计算两个数的异化
        for(int i = 0; i < n; i++){
            for(int j = i; j < n; j++){
                hash[nums[i] ^ nums[j]] = true;
            }
        }

        vector<int> hash3(N);
        // 计算3个数的异或
        for(int j = 0; j < N; j++){
            if(hash[j]){
                for(int i = 0; i < n; i++){
                    hash3[nums[i] ^ j] = true;
                }
            }
        }
        return reduce(hash3.begin(), hash3.end());
    }
};
python 复制代码
#Python
class Solution:
    def uniqueXorTriplets(self, nums: List[int]) -> int:
        N = 1 << (max(nums).bit_length() + 1)
        a = [False] * N
        for x in nums:
            for y in nums:
                a[x ^ y] = True
        b = [0] * N
        for xy, flag in enumerate(a):
            if flag:
                for z in nums:
                    b[xy^z] = 1
        return sum(b)

四、带权树中的最短路径

本题既需要修改边的权重,同时又要计算结点到根的路径长度。一旦我们修改一条边的权重,那么这条边连接的子树的所有结点,到根结点的距离都会有相同的变化,但是放在一棵树上我们很难去维护,如何做?

  • 观察一棵树的先序遍历结果,我们会发现,同一个子树的结点在先序遍历中是连续的,如下图

  • 对一个连续区间的加减操作,我们可以用 差分数组 来维护,同时由于题目还要求能快速查询结点到根节点的距离,我们可以将 差分数组树状数组 进行结合,就能快速进行修改和查询的工作了

代码如下

cpp 复制代码
// C++
class BIT{
public:
    BIT(int n): t(n + 1){}
    void update(int i, int val){
        while(i < t.size()){
            t[i] += val;
            i += i & -i;
        }
    }
    long long pre_sum(int i){
        long long res = 0;
        while(i){
            res += t[i];
            i -= i & -i;
        }
        return res;
    }
private:
    vector<long long> t;
};
class Solution {
public:
    vector<int> treeQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) {
        vector<vector<int>> g(n + 1);
        for(auto& e : edges){
            g[e[0]].push_back(e[1]);
            g[e[1]].push_back(e[0]);
        }
        // 用 [in[i], out[i]] 标记子树在先序遍历中的区间范围 
        vector<int> in(n + 1), out(n + 1);
        int clock = 0;
        auto dfs = [&](this auto&& dfs, int x, int fa)->void{
            in[x] = ++clock;
            for(int y : g[x]){
                if(y == fa) continue;
                dfs(y, x);
            }
            out[x] = clock;
        };
        dfs(1, 0);
        vector<int> w(n + 1);
        BIT t(n);
        auto update = [&](int x, int y, int val){
            if(in[x] < in[y]) swap(x, y); // 确认子树的根节点
            int d = val - w[x]; // 计算变化量
            w[x] = val; // 记录修改之后的值,方便后面计算变化量
            // 差分数组的维护方法
            t.update(in[x], d);
            t.update(out[x] + 1, -d);
        };
        for(auto & e : edges){
            update(e[0], e[1], e[2]);
        }
        vector<int> ans;
        for(auto& q : queries){
            if(q[0] == 1){
                update(q[1], q[2], q[3]);
            }else{
                ans.push_back(t.pre_sum(in[q[1]]));
            }
        }
        return ans;
    }
};
python 复制代码
#Python
class BIT:
    def __init__(self, n:int):
        self.t = [0] * (n + 1)
    
    def update(self, i:int, val:int):
        while i < len(self.t):
            self.t[i] += val
            i += i & -i
    
    def per_sum(self, i:int)->int:
        res = 0
        while i > 0:
            res += self.t[i]
            i -= i & -i
        return res
class Solution:
    def treeQueries(self, n: int, edges: List[List[int]], queries: List[List[int]]) -> List[int]:
        g = [[] for _ in range(n + 1)]
        for x, y, _ in edges:
            g[x].append(y)
            g[y].append(x)
        
        in_ = [0] * (n + 1)
        out = [0] * (n + 1)
        clock = 0
        def dfs(x:int, fa:int)->None:
            nonlocal clock
            clock += 1
            in_[x] = clock
            for y in g[x]:
                if y != fa:
                    dfs(y, x)
            out[x] = clock
        dfs(1, 0)

        t = BIT(n)
        weight = [0] * (n + 1)
        def update(x:int, y:int, val:int)->None:
            if in_[x] < in_[y]:
                x, y = y, x
            d = val - weight[x]
            weight[x] = val
            t.update(in_[x], d)
            t.update(out[x] + 1, -d)
        
        for x, y, w in edges:
            update(x, y, w)
        ans = []
        for q in queries:
            if q[0] == 1:
                update(q[1], q[2], q[3])
            else:
                ans.append(t.per_sum(in_[q[1]]))
        return ans
相关推荐
沐墨专攻技术1 小时前
《空间复杂度(C语言)》
c语言·数据结构·算法·空间复杂度
wuqingshun3141592 小时前
蓝桥杯 6. k倍区间
c++·算法·职场和发展·蓝桥杯·深度优先
明月看潮生3 小时前
青少年编程与数学 02-016 Python数据结构与算法 29课题、自然语言处理算法
python·算法·青少年编程·自然语言处理·编程与数学
xxjiaz4 小时前
二分查找-LeetCode
java·数据结构·算法·leetcode
爱的叹息5 小时前
【java实现+4种变体完整例子】排序算法中【插入排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格
java·算法·排序算法
爱的叹息6 小时前
【java实现+4种变体完整例子】排序算法中【快速排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格
java·算法·排序算法
C灿灿数模6 小时前
2025mathorcup妈妈杯数学建模挑战赛C题:汽车风阻预测,详细思路,模型,代码更新中
人工智能·算法·ffmpeg
周Echo周6 小时前
16、堆基础知识点和priority_queue的模拟实现
java·linux·c语言·开发语言·c++·后端·算法
东雁西飞7 小时前
MATLAB 控制系统设计与仿真 - 39
开发语言·算法·matlab·自动化·工业机器人