LC 2846. 边权重均等查询

2024-01-27 20:20
文章标签 查询 权重 lc 均等 2846

本文主要是介绍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;// addfunction<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// dfsfunction<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;}; // dfsfor (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)

这篇关于LC 2846. 边权重均等查询的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/651383

相关文章

MySQL存储过程之循环遍历查询的结果集详解

《MySQL存储过程之循环遍历查询的结果集详解》:本文主要介绍MySQL存储过程之循环遍历查询的结果集,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言1. 表结构2. 存储过程3. 关于存储过程的SQL补充总结前言近来碰到这样一个问题:在生产上导入的数据发现

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

MYSQL查询结果实现发送给客户端

《MYSQL查询结果实现发送给客户端》:本文主要介绍MYSQL查询结果实现发送给客户端方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql取数据和发数据的流程(边读边发)Sending to clientSending DataLRU(Least Rec

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)

python编写朋克风格的天气查询程序

《python编写朋克风格的天气查询程序》这篇文章主要为大家详细介绍了一个基于Python的桌面应用程序,使用了tkinter库来创建图形用户界面并通过requests库调用Open-MeteoAPI... 目录工具介绍工具使用说明python脚本内容如何运行脚本工具介绍这个天气查询工具是一个基于 Pyt

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查

MySQL基本查询示例总结

《MySQL基本查询示例总结》:本文主要介绍MySQL基本查询示例总结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Create插入替换Retrieve(读取)select(确定列)where条件(确定行)null查询order by语句li

MySQL中like模糊查询的优化方案

《MySQL中like模糊查询的优化方案》在MySQL中,like模糊查询是一种常用的查询方式,但在某些情况下可能会导致性能问题,本文将介绍八种优化MySQL中like模糊查询的方法,需要的朋友可以参... 目录1. 避免以通配符开头的查询2. 使用全文索引(Full-text Index)3. 使用前缀索