https://ac.nowcoder.com/acm/contest/6944/B
题意
一个有根树模型,根为1,包含点权。
对每个节点u,子树任意LCA(i,j)
是u的两点,求最大的GCD(i,j)
,以及最大情况下ij
取法的方案数。
思路
- 注意到这个题点权的范围和n相同的。
- 所有数枚举其因子,这些因子放在一个set里,size也不会超过最大数据范围。
- 统计gcd其实本质就是统计因子。
- 用一个结构统计子树所有数分解以后因子个数。
- 计算节点答案就是一些递推累加。
- 想到启发式合并优化一下,复杂度就差不多了。
- O ( N ( a i + l o g ( N ) ) ) O(N(\sqrt{a_i} + log(N))) O(N(ai+log(N)))
挺好的启发式合并练手题。
参考代码
vector<int> g[MAXN];
map<ll, ll> p[MAXN];
ll val[MAXN];
ll cnt[MAXN];
void dfs(int u, int f) {
for (auto v: g[u]) {
if (v == f) continue;
dfs(v, u);
if (p[u].size() < p[v].size()) {
p[u].swap(p[v]);
}
for (auto pi : p[v]) {
int x = pi.first;
if (p[u].count(x) == 0) {
p[u].emplace(pi);
continue;
}
if (x > val[u]) {
val[u] = x;
cnt[u] = p[u][x] * p[v][x];
} else if (x == val[u]) {
cnt[u] += p[u][x] * p[v][x];
}
p[u][x] += p[v][x];
}
}
// debug(u, p[u]);
}
void solve(int kaseId = -1) {
int n;
cin >> n;
for (int i = 2, u, v; i <= n; ++i) {
cin >> u >> v;
g[u].emplace_back(v);
g[v].emplace_back(u);
}
for (ll i = 1, ai; i <= n; ++i) {
cin >> ai;
for (ll j = 1; j * j <= ai; ++j) {
if (ai % j == 0) {
p[i].emplace(j, 1);
p[i].emplace(ai / j, 1);
}
}
}
dfs(1, -1);
for (int i = 1; i <= n; ++i) {
cout << val[i] << " " << cnt[i] << endl;
}
}