题目链接:https://cn.vjudge.net/problem/HDU-3078
题意:给定一棵树,树上每个点都有权值,输入三个数有两种操作: k a b,k == 0时把a点的权值改为b,k > 0时求a到b路径上第k大的权值
- ,将 点的权值改为 。
- ,求 点到 点路径上的第 大权值。
思路:时直接修改就行了,时,考虑到 点到 点路径上的第 大权值,我们可以把 点到 点路径上的权值全部存起来,然后排序取第 个即可,不好直接求 点到 点路径上的权值,我们可以求出 点 和 点的最近公共祖先 ,然后分别求 点到 点和 点到 点路径上的权值。看似暴力实则可行。
#include <bits/stdc++.h>
using namespace std;
const int N = 8e4 + 7;
int n, m, lg[N], deep[N], fa[N][20];
int a[N], b[N], cnt;
#define pii pair <int, int>
vector <int> E[N];
bool cmp(int a, int b) {return a > b;}
void dfs(int x, int fath)
{
fa[x][0] = fath;
deep[x] = deep[fath] + 1;
for(int i = 1; (1 << i) <= n; i++)
fa[x][i] = fa[fa[x][i-1]][i-1];
for(int i = 0; i < E[x].size(); i++)
{
if(E[x][i] != fath)
dfs(E[x][i], x);
}
}
int lca(int x, int y)
{
if(deep[y] > deep[x]) swap(x, y);
while(deep[x] > deep[y])
x = fa[x][lg[deep[x]-deep[y]]];
if(x == y) return x;
for(int i = lg[deep[x]]; i >= 0; i--)
if(fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
void solve(int s, int e)
{
while(s != e)
{
b[cnt++] = a[s];
s = fa[s][0];
}
}
int main()
{
scanf("%d%d",&n, &m);
for(int i = 1; i <= n; i++)
scanf("%d",&a[i]);
for(int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d",&u, &v);
E[u].push_back(v);
E[v].push_back(u);
}
lg[0] = -1;
for(int i = 1; i <= n; i++)
lg[i] = lg[i >> 1] + 1;
dfs(1, 0);
while(m--)
{
int x, y, ans, k;
scanf("%d%d%d",&k, &x, &y);
if(k == 0) a[x] = y;
else
{
int rt = lca(x, y); cnt = 0;
solve(x, rt); solve(y, rt);
b[cnt++] = a[rt];
sort(b, b + cnt, cmp);
if(k > cnt) puts("invalid request!");
else printf("%d\n",b[k-1]);
}
}
}