题目链接:
[CodeForces 593D]Happy Tree Party[LCA][路径压缩]
题意分析:
给出两个操作,1和2。
1代表查询结点a到结点b之间最短路径,将路径上的权值对y进行向下求整,输出结果。
2更改某个结点的权值
解题思路:
y最大1e18,对于除数为大于1的数字来说,最多除60次就可以让y变成0了。所以用lca不断除,当结果是0时就返回结果。
不过还要考虑除数为1的情况,这就会使得路径变得很长导致T了。所以要路径压缩一下。
个人感受:
比赛的时候知道LCA,但是觉得会T,没考虑过向下除不超过60次就变0了。
具体代码如下:
#include<cstdio>
#include<iostream>
#include<queue>
#define ll long long
#define pr(x) cout << #x << " = " << (x) << '\n';
using namespace std;
const int INF = 0x7f7f7f7f;
const int MAXN = 2e5 + 111;
struct Node{
int v, id;
ll w;
Node(int _v, ll _w, int _id): v(_v), w(_w), id(_id) {}
};
int dep[MAXN], fa[MAXN], par[MAXN], point[MAXN]; // point:点所对应的边id
ll val[2 * MAXN];
vector<Node> G[MAXN];
int find(int x)
{
return x == par[x] ? x : par[x] = find(par[x]);
}
void dfs(int u, int p, int d)
{
dep[u] = d;
fa[u] = p;
for (int i = 0; i < G[u].size(); ++i)
{
Node &e = G[u][i];
if (e.v != p)
{
val[e.v] = e.w; // 将点所对应的权值转换为以这个结点为结尾的边对应的权值来记录
point[e.id] = e.v; // 用id来查找点
dfs(e.v, u, d + 1);
}
}
}
ll lca(int u, int v, ll w)
{
u = find(u), v = find(v);
while (u != v) // 每次将较低的结点向上提升,不断除
{
if (dep[u] < dep[v]) swap(u, v);
w /= val[u]; u = find(fa[u]);
if (w == 0) return 0;
}
return w;
}
void update(int u) // 如果该点权值为一,那么就让它的祖先指向它的父亲结点
{
par[u] = fa[u];
}
int main()
{
int n, m, u, v, op;
ll w;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) G[i].clear(), par[i] = i;
for (int i = 1; i < n; ++i)
{
scanf("%d%d%I64d", &u, &v, &w);
G[u].push_back(Node(v, w, i));
G[v].push_back(Node(u, w, i));
}
dfs(1, -1, 0);
for (int i = 2; i <= n; ++i) if (val[i] == 1) update(i);
int a, b, p;
ll y, c;
while (m --)
{
scanf("%d", &op);
if (op == 1)
{
scanf("%d%d%I64d", &a, &b, &y);
printf("%I64d\n", lca(a, b, y));
}
else
{
scanf("%d%I64d", &p, &c);
int v = point[p]; val[v] = c;
if (c == 1) update(v);
}
}
return 0;
}