LC 2846. 边权重均等查询

2846. 边权重均等查询

难度: 困难

题目大意:

现有一棵由 n 个节点组成的无向树,节点按从 0n - 1 编号。给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ui, vi, wi] 表示树中存在一条位于节点 ui 和节点 vi 之间、权重为 wi 的边。

另给你一个长度为 m 的二维整数数组 queries ,其中 queries[i] = [ai, bi] 。对于每条查询,请你找出使从 aibi 路径上每条边的权重相等所需的 最小操作次数 。在一次操作中,你可以选择树上的任意一条边,并将其权重更改为任意值。

注意:

  • 查询之间 相互独立 的,这意味着每条新的查询时,树都会回到 初始状态
  • aibi的路径是一个由 不同 节点组成的序列,从节点 ai 开始,到节点 bi 结束,且序列中相邻的两个节点在树中共享一条边。

返回一个长度为 m 的数组 answer ,其中 answer[i] 是第 i 条查询的答案。

提示:

  • 1 <= n <= 10^4
  • edges.length == n - 1
  • edges[i].length == 3
  • 0 <= ui, vi < n
  • 1 <= wi <= 26
  • 生成的输入满足 edges 表示一棵有效的树
  • 1 <= queries.length == m <= 2 * 10^4
  • queries[i].length == 2
  • 0 <= ai, bi < n

示例 1:
请添加图片描述

输入:n = 7, edges = [[0,1,1],[1,2,1],[2,3,1],[3,4,2],[4,5,2],[5,6,2]], queries = [[0,3],[3,6],[2,6],[0,6]]
输出:[0,0,1,3]
解释:第 1 条查询,从节点 0 到节点 3 的路径中的所有边的权重都是 1 。因此,答案为 0 。
第 2 条查询,从节点 3 到节点 6 的路径中的所有边的权重都是 2 。因此,答案为 0 。
第 3 条查询,将边 [2,3] 的权重变更为 2 。在这次操作之后,从节点 2 到节点 6 的路径中的所有边的权重都是 2 。因此,答案为 1 。
第 4 条查询,将边 [0,1]、[1,2]、[2,3] 的权重变更为 2 。在这次操作之后,从节点 0 到节点 6 的路径中的所有边的权重都是 2 。因此,答案为 3 。
对于每条查询 queries[i] ,可以证明 answer[i] 是使从 ai 到 bi 的路径中的所有边的权重相等的最小操作次数。

分析

如果暴力写的话, 那么对于每一个查询,我们要dfs一遍,每一遍存一下路径上的边权得数量,最后用总的数量减去最多的变得数量就是答案,这是一个小贪心的思路,那么考虑一下数据范围,如果暴力写的话,时间复杂度是 O ( n 2 ) O(n^2) O(n2),肯定会超时的,但是也吧暴力写法的代码贴出来。

723 / 733 个通过的测试用例

暴力 dfs (会超时)

class Solution {
public:
    vector<int> minOperationsQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) {
        int m = queries.size();
        vector<int> e(n << 1), ne(n << 1), h(n, -1), w(n << 1), ans(m); // 链式向前星
        int cnt[27], idx = 0;
        
        // add
        function<void(int, int, int)> add = [&](int a, int b, int c) {
            e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
            e[idx] = a, ne[idx] = h[b], w[idx] = c, h[b] = idx ++;
        }; // add
        
        // dfs
        function<bool(int, int, int)> dfs = [&](int u, int b, int fa) {
            if (u == b) {
                return true;
            }
            for (int i = h[u]; ~i; i = ne[i]) {
                int j = e[i];
                if (j == fa) continue;
                if (dfs(j, b, u)) {
                    ++ cnt[w[i]];
                    return true;
                }
            }
            return false;
        }; // dfs
        
        for (int i = 0; i < n - 1; i ++ ) {
            int a = edges[i][0], b = edges[i][1], w = edges[i][2];
            add(a, b, w);
        }
       	
        for (int i = 0; i < m; i ++) {
            memset(cnt, 0, sizeof cnt); // 每次清空数组
            int a = queries[i][0], b = queries[i][1];
            dfs(a, b, -1);
            int res = 0, sum = 0;
            for (int i = 1; i <= 26; i ++) {
                sum += cnt[i];
                res = max(res, cnt[i]);
            }
            ans[i] = sum - res;
        }
        return ans;
    }
};

时间复杂度: O ( n ∗ m ∗ W ) O(n*m*W) O(nmW) (本题 W = 26)

分析

我们可以用最近公共祖先的思想,选定一个根节点,假设是0,那么定义一个cnt[i][w]表示节点i到根节点的路径中边权为w(1 <= w <= 26)的边的数量,那么ij之间边权为w的边数是 t a = c n t [ i ] [ w ] + c n t [ j ] [ w ] − 2 ∗ c n t [ l c a ( i , j ) ] [ w ] t_a = cnt[i][w] + cnt[j][w] - 2 * cnt[lca(i, j)][w] ta=cnt[i][w]+cnt[j][w]2cnt[lca(i,j)][w]lca(i, j)表示节点i和节点j的最近公共祖先, 那么要替换的边数就是
∑ i = 1 26 t i − max ⁡ 1 < = i < = 26 t i \sum_{i = 1}^{26} {t_i} - \max_{1 <= i <= 26}t_i i=126ti1<=i<=26maxti
使用离线算法tarjan算法模板

tarjan + 并查集

class Solution {
public:
    using PII = pair<int, int>;
    vector<int> minOperationsQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) {
        int m = queries.size();
        vector<unordered_map<int, int>> g(n);
        for (auto& e : edges) {
            g[e[0]][e[1]] = e[2];
            g[e[1]][e[0]] = e[2];
        }
        vector<vector<PII>> q(n);   
        for (int i = 0; i < m; i ++ ){
            q[queries[i][0]].push_back({queries[i][1], i});
            q[queries[i][1]].push_back({queries[i][0], i});
        }
        vector<int> lca(m), vis(n), p(n);
        iota(p.begin(), p.end(), 0);
        vector<vector<int>> cnt(n, vector<int>(27));
        function<int(int)> find = [&](int x) {
            if (x != p[x]) p[x] = find(p[x]);
            return p[x];
        };
        function<void(int, int)> tarjan = [&](int u, int fa) {
            if (fa != -1) {
                cnt[u] = cnt[fa];
                ++ cnt[u][g[u][fa]];
            }
            p[u] = u;
            for (auto& e : g[u]) {
                if (e.first == fa) continue;
                tarjan(e.first, u);
                p[e.first] = u;
            }
            for (auto& e : q[u]) {
                if (u != e.first && !vis[e.first]) continue;
                lca[e.second] = find(e.first);
            }
            vis[u] = 1;
        };
        tarjan(0, -1);
        vector<int> res(m);
        for (int i = 0; i < m; i ++ ){
            int sum = 0, mx = 0;
            for (int j = 1; j <= 26;j ++) {
                int t = cnt[queries[i][0]][j] + cnt[queries[i][1]][j] - 2 * cnt[lca[i]][j];
                mx = max(mx, t);
                sum += t;
            }
            res[i] = sum - mx;
        }
        return res;
    }
};

时间复杂度: O ( ( m + n ) × W + m × l o g n ) O((m+n)×W+m×logn) O((m+n)×W+m×logn) (本题 W = 26)

在线lca算法

const int N = 10010;
class Solution {
public:
    int e[N << 1], ne[N << 1], w[N << 1], h[N],  idx;
    int fa[N][15], depth[N];
    int cnt[N][27], cntn[27];
    int q[N];

    void bfs() {
        int hh = 0, tt = 0;
        q[0] = 1;
        memset(depth, 0x3f, sizeof depth);
        depth[0] = 0, depth[1] = 1;
        while (hh <= tt) {
            int t = q[hh ++ ];
            for (int i = h[t]; ~i; i = ne[i]) {
                int j = e[i];
                if (depth[j] > depth[t] + 1) {
                    depth[j] = depth[t] + 1;
                    q[ ++ tt] = j;
                    fa[j][0] = t;
                    for (int k = 1; k <= 14; k ++ )
                        fa[j][k] = fa[fa[j][k - 1]][k - 1];
                }
            }
        }
    }
    // dfs版本
	void dfs_dep(int u, int father) {
        depth[u] = depth[father] + 1;
        fa[u][0] = father;
        for (int i = 1; i <= 14; i ++) 
            fa[u][i] = fa[fa[u][i - 1]][i - 1];
        for (int i = h[u]; ~i; i = ne[i]) {
            if (e[i] != father) {
                dfs_dep(e[i], u);
            }
        }
    }
    
    void add(int a, int b, int c) {
        e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++ ;
    }

    void dfs(int u, int fa) {
        memcpy(cnt[u], cntn, sizeof cntn);
        for (int i = h[u]; ~i; i = ne[i]) {
            int j = e[i];
            if (fa == j) continue;
            cntn[w[i]] ++;
            dfs(j, u);
            cntn[w[i]] -- ;
        }
    }

    int lca(int a, int b){
        if (depth[a] < depth[b]) swap(a, b);
        for (int k = 14; k >= 0; k -- )
            if (depth[fa[a][k]] >= depth[b]) 
                a = fa[a][k];
        if (a == b) return a;

        for (int k = 14; k >= 0; k -- ) {
            if (fa[a][k] != fa[b][k]) {
                a = fa[a][k];
                b = fa[b][k];
            }
        }
        return fa[a][0];
    }

    vector<int> minOperationsQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) {
        memset(h, -1,sizeof h);
        for (int i = 0; i < edges.size(); i ++ ) {
            int a = edges[i][0], b = edges[i][1], c = edges[i][2];
            a ++, b ++ ;
            add(a, b, c), add(b, a, c);
        }
        bfs();
        // dfs_dep(1, 0); // dfs_dep版本
        dfs(1, -1);
        vector<int> ans(queries.size());
        for (int i = 0; i < queries.size(); i ++ ) {
            int a = queries[i][0], b = queries[i][1];
            a ++, b ++ ;
            int p = lca(a, b);
            vector<int> s(27);
            for (int j = 1; j <= 26; j ++ )
                s[j] += cnt[a][j] + cnt[b][j] - cnt[p][j] * 2;
            int sum = 0, maxv = 0;
            for (int j = 1; j <= 26; j ++ ) {
                maxv = max(maxv, s[j]);
                sum += s[j];
            }
            ans[i] = sum - maxv;
        }
        return ans;
    }
};

时间复杂度: O ( m l o g n ) O(mlogn) O(mlogn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/351155.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

银行数据仓库体系实践(11)--数据仓库开发管理系统及开发流程

数据仓库管理着整个银行或公司的数据&#xff0c;数据结构复杂&#xff0c;数据量庞大&#xff0c;任何一个数据字段的变化或错误都会引起数据错误&#xff0c;影响数据应用&#xff0c;同时业务的发展也带来系统不断升级&#xff0c;数据需求的不断增加&#xff0c;数据仓库需…

EventSource 长链接执行

EventSource 说明文档MDN 其他参考文档 一、利用node启服务 import fs from fs import express from express const app express() // eventSource 仅支持 get 方法 // 服务器端发送的数据必须是纯文本格式&#xff0c;不能是二进制数据。 app.get(/api, (req, res) > …

项目性能优化之用compression-webpack-plugin插件开启gzip压缩

背景&#xff1a;vue项目打包发布后&#xff0c;部分js、css文件体积较大导致页面卡顿&#xff0c;于是使用webpack插件compression-webpack-plugin开启gzip压缩 前端配置vue.config.js 先通过npm下载compression-webpack-plugin包&#xff0c;npm i compression-webpack-plug…

Android SharedPreferences源码分析

文章目录 Android SharedPreferences源码分析概述基本使用源码分析获取SP对象初始化和读取数据写入数据MemoryCommitResultcommitToMemory()commit()apply()enqueueDiskWrite()writeToFile() 主动等待写回任务结束 总结 Android SharedPreferences源码分析 概述 SharedPrefer…

EXCEL VBA抓取网页JSON数据并解析

EXCEL VBA抓取网页JSON数据并解析 链接地址&#xff1a; https://api.api68.com/CQShiCai/getBaseCQShiCaiList.do?lotCode10036&date2024-01-26 Sub test() On Error Resume Next Sheet.Select Sheet1.Cells.ClearContents [a1:g1] Split("preDrawIssue|preDrawTi…

Ubuntu20.04添加桌面启动、侧边栏启动和终端启动

桌面启动 新建XX.desktop文件 在桌面新建一个XX.desktop文件&#xff0c;以QtCreator为例。 &#xff08;注意这里不能使用sudo&#xff0c;因为这样会把文件的权限归为root&#xff0c;导致后续设置可执行程序不方便&#xff09; gedit qtcreator.desktop在XX.desktop文件中…

第14次修改了可删除可持久保存的前端html备忘录:增加一个翻牌钟,修改背景主题:现代深色

第14次修改了可删除可持久保存的前端html备忘录&#xff1a;增加一个翻牌钟&#xff0c;修改背景主题&#xff1a;现代深色 备忘录代码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta http-equiv"X…

设计模式—行为型模式之责任链模式

设计模式—行为型模式之责任链模式 责任链&#xff08;Chain of Responsibility&#xff09;模式&#xff1a;为了避免请求发送者与多个请求处理者耦合在一起&#xff0c;于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链&#xff1b;当有请求发生时&am…

Flink面试题

0. 思维导图 1. 简单介绍一下Flink♥♥ Flink是一个分布式的计算框架&#xff0c;主要用于对有界和无界数据流进行有状态计算&#xff0c;其中有界数据流就是值离线数据&#xff0c;有明确的开始和结束时间&#xff0c;无界数据流就是指实时数据&#xff0c;源源不断没有界限&a…

ES文档索引、查询、分片、文档评分和分析器技术原理

技术原理 索引文档 索引文档分为单个文档和多个文档。 单个文档 新建单个文档所需要的步骤顺序&#xff1a; 客户端向 Node 1 发送新建、索引或者删除请求。节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3&#xff0c;因为分片 0 的主分片目前被分配在 …

Android源码设计模式解析与实战第2版笔记(二)

第二章 应用最广的模式 — 单例模式 单例模式的定义 确保某一个类只有一个实例&#xff0c;而且自行实例化并向整个系统提供这个实例。 单例模式的使用场景 确保某个类有且只有一个对象的场景&#xff0c;避免产生多个对象消耗过多的资源&#xff0c;或者某种类型的对象只应…

DT浏览器浏览网页的技巧

DT浏览器浏览网页的技巧&#xff1a; 1. 使用书签和收藏夹&#xff1a;将常用的网页添加到书签或收藏夹中&#xff0c;方便快速访问。 2. 善用搜索引擎&#xff1a;使用搜索引擎可以快速找到你需要的信息。在搜索时&#xff0c;可以使用关键词和筛选条件来精确查找。 3. 注意网…

【C/C++】详解程序环境和预处理(什么是程序环境?为什么要有程序环境?如何理解程序环境?)

目录 一、前言 二、 什么是程序环境&#xff1f; 三、 为什么要有程序环境&#xff1f; 四、如何理解程序环境&#xff1f; &#x1f34e; ANSI C 标准 &#x1f350; 翻译环境和执行环境 五、详解翻译环境和执行环境 &#x1f347;翻译环境&#xff08;重点&#xff01…

米贸搜|Meta金融科技行业广告政策Facebook

当前全球金融科技行业正处于快速发展阶段&#xff0c;作为全球最大的社交媒体网络之一&#xff0c;Meta平台为金融科技公司提供了一个重要的业务营销渠道&#xff0c;使其能够在广大用户群体中宣传和推广其产品和服务。 金融科技企业通过Meta平台投放广告&#xff0c;要注重政策…

Mac网线上网绿联扩展坞连接网线直接上网-无脑操作

声明&#xff1a;博主使用的绿联扩展坞 以下为绿联扩展坞Mac网线使用方法 1.首先需要下载电脑对应版本的驱动 直接点击即可下载 2. 下载好以后 解压 点进去 对应版本 博主直接使用最新的12-14 3. 安装包好了以后 会提示重启电脑 此时拔掉扩展坞 再重启动 拔掉扩展坞 再重启…

【机组】单元模块实验的综合调试与驻机键盘和液晶显示器的使用方式

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 1. 综合实验的调试 1.1 实验…

three.js 鼠标选中模型弹出标签

效果&#xff1a;请关注抖音 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red;position: relative;"></div><…

Emergent Abilities of Large Language Models 机翻mark

摘要 证明通过扩大语言模型可以可靠地提高性能和样本效率在广泛的下游任务。相反&#xff0c;本文讨论了我们称之为大型语言模型的新兴能力的一种不可预测的现象。我们认为如果一个能力不存在于较小的模型中&#xff0c;但在较大的模型中存在&#xff0c;则该能力就是新兴的。…

springboot 整合 Activiti6

1.添加maven依赖 <dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter-basic</artifactId><version>6.0.0</version> </dependency>2.添加配置 spring:activiti:check-process-definitio…

【wink】如何调整视频比例、缩放视频、修改背景?

这三个问题都可在「视频剪辑-画布」功能里实现&#xff1a; 调整视频比例&#xff1a; 点击「视频剪辑 - 画布- 比例」&#xff0c;即可选择合适的视频比例。 缩放视频&#xff1a; 点击「视频剪辑 - 画布 - 缩放」&#xff0c;即可通过滑杆缩放视频。 修改背景&#xff1a;…