题意:给出一个有n(n<=200000)的树形图,每条边有一个权值。有两种操作,1是将一个边的权值变小,
2是给定两点a,b和一个值y,用y(long long范围内)依次除以两点之间的路径上的所有边的权值,每次除法都是向下取整。
并输出这个值。
操作次数为m(m<=200000)。
分析:
这是一个较难的在线LCA问题。
首先,每个y最多经过63次>=2的除数的除法就会变为0。
建树并生成LCA。
每次处理第2种请求时,我们都是先求两点的LCA,再分别处理两点到其LCA的路径。
对于从低到高走路径的过程中,连续的权值为1的边,我们用一种类似于并查集的方式将他们缩成一条边。
例如:a是b的父亲,边权值为1。那么我们领link[b]=a。否则我们令link[b]=b。
这样如果出现连续的权值为1的边,我们不断追溯其link值直到与自身相等即可。记得使用并查集的路径压缩优化。
那么我们处理单个路径时最多需要处理的边数约为63×2(权值>=2的和=1的边交替出现的情况)。
所以总复杂度为O(mlogy)
#include <cstdio> #include <algorithm> #include <cstring> #include <iostream> #include <queue> using namespace std; #define d(x) const int MAX_NODE_NUM = (int)(2e5) + 100; const int M = 30; int n, m; vector<pair<int, long long> > edge[MAX_NODE_NUM]; bool vis[MAX_NODE_NUM]; int father[MAX_NODE_NUM][M]; int depth[MAX_NODE_NUM]; long long value[MAX_NODE_NUM]; pair<int, int> p_edge[MAX_NODE_NUM]; int link[MAX_NODE_NUM]; void bfs(int root) { queue<int> q; q.push(root); while (!q.empty()) { int u = q.front(); q.pop(); vis[u] = true; for (int i = 0; i < (int)edge[u].size(); i++) { int v = edge[u][i].first; if (vis[v]) { continue; } father[v][0] = u; depth[v] = depth[u] + 1; value[v] = edge[u][i].second; link[v] = v; if (u != 0 && value[v] == 1) link[v] = u; q.push(v); } } } //index start from 1. void init_LCA(int root) { fill_n(vis, n + 1, 0); memset(father, 0, sizeof(father)); bfs(root); bool did; for (int i = 1; i < M; i++) { did = false; for (int j = 1; j <= n; j++) { int k = father[j][i - 1]; if (k <= 0) { continue; } father[j][i] = father[k][i - 1]; did = true; } if (!did) { break; } } } //O(log(n)) int LCA(int x, int y) { if (depth[x] > depth[y]) { swap(x, y); } int diff = depth[y] - depth[x]; for (int i = 0; i < M && diff; i++) { if (diff & 1) { y = father[y][i]; } diff >>= 1; } if (x == y) { return x; } int exp = 0; while (x != y) { if (!exp || father[x][exp] != father[y][exp]) { x = father[x][exp]; y = father[y][exp]; exp++; }else { exp--; } } return x; } long long next_ll() { long long a; scanf("%lld", &a); return a; } void input() { scanf("%d%d", &n, &m); for (int i = 0; i < n - 1; i++) { int a, b; long long c; scanf("%d%d", &a, &b); c = next_ll(); edge[a].push_back(make_pair(b, c)); edge[b].push_back(make_pair(a, c)); p_edge[i] = make_pair(a, b); } } int get_link(int a) { if (a == link[a]) return a; return link[a] = get_link(link[a]); } void make(int a, int lca, long long &w) { while (w > 0 && depth[a] > depth[lca]) { w /= value[a]; a = father[a][0]; a = get_link(a); } } void work(int a, int b, long long w) { int lca = LCA(a, b); make(a, lca, w); make(b, lca, w); printf("%lld ", w); } void work(int p, long long w) { p--; int u = p_edge[p].first; int v = p_edge[p].second; if (depth[u] > depth[v]) { swap(u, v); } value[v]= w; if (w == 1) { link[v] = link[u]; } } int main() { input(); init_LCA(1); for (int i = 0; i < m; i++) { int command; int a, b; long long c; scanf("%d", &command); if (command == 1) { scanf("%d%d", &a, &b); c = next_ll(); work(a, b, c); }else { scanf("%d", &a); c = next_ll(); work(a, c); } } return 0; }