目录
一、题目
1、题目描述
现有一棵由
n
个节点组成的无向树,节点按从0
到n - 1
编号。给你一个整数n
和一个长度为n - 1
的二维整数数组edges
,其中edges[i] = [ui, vi, wi]
表示树中存在一条位于节点ui
和节点vi
之间、权重为wi
的边。另给你一个长度为
m
的二维整数数组queries
,其中queries[i] = [ai, bi]
。对于每条查询,请你找出使从ai
到bi
路径上每条边的权重相等所需的 最小操作次数 。在一次操作中,你可以选择树上的任意一条边,并将其权重更改为任意值。注意:
- 查询之间 相互独立 的,这意味着每条新的查询时,树都会回到 初始状态 。
- 从
ai
到bi
的路径是一个由 不同 节点组成的序列,从节点ai
开始,到节点bi
结束,且序列中相邻的两个节点在树中共享一条边。返回一个长度为
m
的数组answer
,其中answer[i]
是第i
条查询的答案。
2、接口描述
cpp
class Solution {
public:
vector<int> minOperationsQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) {
}
};
3、原题链接
二、解题报告
1、思路分析
关于ST表见:高级搜索------ST表,离线RMQ问题-CSDN博客
关于倍增法求LCA见:LCA算法-倍增算法-CSDN博客
对于树中任意两个点之间的路径我们都可以转化为两点各自到lca的路径拼接,所以这是一道典型的lca问题
所以不难求出a到b的路径,那么最小操作次数怎么求呢?对于路径上的边权我们发现取值在[1,26]之间,如果路径上边权的众数出现次数为k,那么最小操作次数就是len(path) - k
因而我们先求LCA,然后对于每个查询我们找出k,然后该查询的答案就是len(path) - k
len(path) = depth(x) +depth(y) - depth(lca) * 2
那么如何求出现次数最多的边权呢?
其实也能用倍增法解决
我们开一个数组cnt[x][i][w],表示节点x向上2^i次方层路径上的边权为w的边数,我们怎么预处理cnt[][][]呢?
在LCA预处理过程中更新即可,我们发现cnt[x][i][w] = cnt[x][i - 1][w] + cnt[fa[x][i - 1]][i - 1][w]
显然也能用倍增法解决
然后后面查询lca的时候记录路径上出现次数最多的边权即可
2、复杂度
时间复杂度: O(U(n + m)logn)空间复杂度:O(Unlogn)
3、代码详解
cpp
typedef pair<int, int> pii;
int f[10010][15], depth[10010], cnt[10010][15][27];
class Solution
{
public:
vector<int> minOperationsQueries(int n, vector<vector<int>> &edges, vector<vector<int>> &queries)
{
memset(f, 0, sizeof(f)), memset(depth, 0, sizeof(depth)), memset(cnt, 0, sizeof(cnt));
vector<int> res;
vector<vector<pii>> g(n + 1);
for (auto &e : edges)
g[e[0] + 1].emplace_back(e[1] + 1, e[2]), g[e[1] + 1].emplace_back(e[0] + 1, e[2]);
function<void(int, int)> dfs = [&](int x, int fa)
{
depth[x] = depth[fa] + 1 , f[x][0] = fa;
for (int i = 1; i < 15; i++)
{
f[x][i] = f[f[x][i - 1]][i - 1];
if (f[x][i - 1])
for (int w = 1; w <= 26; w++)
cnt[x][i][w] = cnt[x][i - 1][w] + cnt[f[x][i - 1]][i - 1][w];
}
for (auto [y, w] : g[x])
if (y != fa)
cnt[y][0][w] = 1, dfs(y, x);
};
dfs(1, 0);
for (auto &q : queries)
{
int x = q[0] + 1, y = q[1] + 1;
int cntw[27]{0}, len = depth[x] + depth[y];
if (depth[x] < depth[y])
swap(x, y);
for (int i = 14; i >= 0; i--)
if (depth[f[x][i]] >= depth[y])
{
for (int w = 1; w <= 26; w++)
cntw[w] += cnt[x][i][w];
x = f[x][i];
}
if (x != y)
{
for (int i = 14; i >= 0; i--)
{
if (f[x][i] != f[y][i])
{
for (int w = 1; w <= 26; w++)
cntw[w] += cnt[x][i][w] + cnt[y][i][w];
x = f[x][i], y = f[y][i];
}
}
for (int w = 1; w <= 26; w++)
cntw[w] += cnt[x][0][w] + cnt[y][0][w];
x = f[x][0];
}
int lca = x;
len -= (depth[lca] << 1);
res.emplace_back(len - *max_element(cntw + 1, cntw + 27));
}
return res;
}
};