Codeforces Round #588 (Div. 2) E. Kamil and Making a Stream(dfs)

题意:

给一棵 \(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;
}
原文地址:https://www.cnblogs.com/ChaseNo1/p/11588231.html