题目链接: http://codeforces.com/problemset/problem/593/D
---------------------------------------------------------------------------------------------------------------
刚看完题目感觉必须用树链剖分之类的方法,就放弃了,然而我们要发现题目具有如下性质:
一个$ long long $以内的数如果被大于$1$的数除 几十次就可以除到$0$了
所以就可以考虑下$LCA$
但如果长度为$1$的边过多 复杂度又不靠谱了 这时我们又会观察到修改时边的长度只减不增
因此对于长度为$1$的边 我们可以进行路径压缩
不过这里的压缩并不是改变原来的树的结构 因为改变树的结构就会麻烦许多
我们只需把以长度为$1$的边相连的点放在一个集合内, 如果集合内的一个点需要向上查找
就直接找到这个集合的顶端
$($尽管集合顶端的点并不一定是两点的$LCA $但由于集合内的边长均为$1$ 实际结果是等价的$)$
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 const int N = 2e5 + 10; 7 int firste[N], nexte[N << 1], v[N << 1]; 8 long long w[N << 1]; 9 int fa[N], d[N], aim[N], fa2[N]; 10 long long dist[N]; 11 int n, m, e = 1; 12 void build_edge(int x, int y, long long z) 13 { 14 nexte[++e] = firste[x]; 15 firste[x] = e; 16 v[e] = y; 17 w[e] = z; 18 } 19 int findf(int x) 20 { 21 if(fa2[x] == x) 22 return x; 23 return fa2[x] = findf(fa2[x]); 24 } 25 void dfs(int u) 26 { 27 for(int p = firste[u]; p; p = nexte[p]) 28 if(!fa[v[p]]) 29 { 30 aim[p / 2] = v[p]; 31 fa[v[p]] = u; 32 d[v[p]] = d[u] + 1; 33 dist[v[p]] = w[p]; 34 if(w[p] == 1) 35 fa2[v[p]] = findf(u); 36 dfs(v[p]); 37 } 38 } 39 long long check(int x, int y, long long z) 40 { 41 while(x != y) 42 { 43 if(d[x] < d[y]) 44 swap(x, y); 45 if(dist[x] == 1) 46 x = findf(x); 47 else 48 { 49 z /= dist[x]; 50 x = fa[x]; 51 if(!z) 52 return z; 53 } 54 } 55 return z; 56 } 57 int main() 58 { 59 scanf("%d%d", &n, &m); 60 int x, y; 61 long long z; 62 for(int i = 1; i < n; ++i) 63 { 64 scanf("%d%d%lld", &x, &y, &z); 65 build_edge(x, y, z); 66 build_edge(y, x, z); 67 } 68 fa[1] = 1; 69 dist[1] = 1; 70 for(int i = 1; i <= n; ++i) 71 fa2[i] = i; 72 dfs(1); 73 while(m--) 74 { 75 scanf("%d", &x); 76 if(x & 1) 77 { 78 scanf("%d%d%lld", &x, &y, &z); 79 printf("%lld ", check(x, y, z)); 80 } 81 else 82 { 83 scanf("%d%lld", &x, &z); 84 int u = aim[x]; 85 dist[u] = z; 86 if(z == 1) 87 fa2[u] = findf(fa[u]); 88 } 89 } 90 return 0; 91 }