题意:
给一棵 \(n\) 个节点的树,定义 \(f(u,v)\),其中 \(u\) 为 \(v\) 的祖先,\(f(u,v)\) 等于 \(u\) \(\rightarrow\) \(v\) 路径上所有点权值的 \(gcd\)。现求一棵树上所有的 \(f(u,v)\) 之和。
分析:
简单的 \(dfs\),难点可能在于不好估计复杂度。一棵树上所有的 \(f(u,v)\) \((u!=v)\) 权值数不会超过 \(log_2(1e12)\) 种,暴力即可。
我们用一个 \(map\) 记录每个点的祖先到其的 \(gcd\) 和到这个点的 \(gcd\) 出现的次数,那么就可以计算贡献了。因为 \(f(u,v)\) 权值数不会超过 \(log_2(1e12)\) 种,所以对每个节点暴力向其孩子求贡献的总复杂度是 \(O(nlog_2(1e12))\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define pb push_back
const int N = 1e5 + 5;
const LL mod = 1e9 + 7;
int n, u, v;
LL ans, w[N];
vector<int> g[N];
unordered_map<LL, int> maps[N];
void add(int u, LL w, int p) {
ans = (ans + 1LL * p * w % mod) % mod;
maps[u][w] += p;
}
void dfs(int u, int f) {
add(u, w[u], 1);
for (auto it : g[u]) {
if (it == f) continue;
for (auto it1 : maps[u]) add(it, __gcd(w[it], it1.first), it1.second);
dfs(it, u);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%lld", w + i);
for (int i = 1; i <= n - 1; i++) {
scanf("%d %d", &u, &v);
g[u].pb(v), g[v].pb(u);
}
dfs(1, -1);
printf("%lld\n", ans);
return 0;
}