考察知识点:树上 DFS、倍增
本题很容易想到模拟的方式,但时间复杂度为 ,会超时。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
const int N = 1e5 + 5;
vector<int> edges[N];
int fa[N], a[N];
void dfs(int u, int from)
{
fa[u] = from;
for (int v : edges[u])
if (v != from)
dfs(v, u);
}
void query(int u, int v, int c)
{
int ans = 0, m = c;
while (u != fa[v])
{
if (a[u] > m)
ans++;
m = max(m, a[u]);
u = fa[u];
}
cout << ans << endl;
}
void solve()
{
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
edges[u].push_back(v);
edges[v].push_back(u);
}
dfs(1, 0);
while (q--)
{
int u, v, c;
cin >> u >> v >> c;
query(u, v, c);
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}
使用 倍增 来优化程序,我们不用每次只向上跳一步,而是每次向上跳 步。
详解如下代码,其中 fa[i][j]
为从 i
点开始,购买了 次珠宝的节点,
dep[i]
为节点 i
到根节点的距离(即深度),to[i]
为第 次询问的终点,
a[n + i]
为第 次询问的初始珠宝价值。
时间复杂度:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
const int N = 2e5 + 5, LOGN = 20;
vector<int> edges[N];
int a[N], fa[N][LOGN];
int dep[N], to[N];
void dfs(int u, int from)
{
dep[u] = dep[from] + 1;
int f0 = from;
// fa[u][0] 为从 u 节点出发,购买了 2^0 = 1 次珠宝的节点
if (a[from] > a[u]) // 如果父节点刚好是满足条件的节点
fa[u][0] = from;
else // 否则向上跳
{
for (int i = LOGN - 1; i >= 0; i--) // 从大到小枚举
if (fa[from][i] && a[fa[from][i]] <= a[u]) // 从父节点开始向上跳
from = fa[from][i];
fa[u][0] = fa[from][0]; // 事实上,fa[u][0] 就是 u 往上走的第一个 a 值比 u 大的节点
}
for (int i = 1; i < LOGN; i++)
fa[u][i] = fa[fa[u][i - 1]][i - 1]; // 2^i = 2^(i-1) + 2^(i-1)
for (int v : edges[u]) // 遍历所有子节点
if (v != f0)
dfs(v, u);
}
void solve()
{
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
edges[u].push_back(v);
edges[v].push_back(u);
}
for (int i = 1; i <= q; i++) // 为了考虑 u 点
{
int u, v, c;
cin >> u >> v >> c;
edges[i + n].push_back(u);
edges[u].push_back(i + n);
to[i] = v, a[i + n] = c;
}
dfs(1, 0);
for (int i = 1; i <= q; i++)
{
int u = n + i, v = to[i], ans = 0;
for (int i = LOGN - 1; i >= 0; i--)
if (dep[fa[u][i]] >= dep[v]) // 尝试向上跳 2^i 步
{
u = fa[u][i];
ans += 1 << i;
}
cout << ans << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}