Problem G. GCD Counting
考虑差分,记录每一个答案 (ans_i) 为 (gcd) 为 (i) 的倍数的方案总数。
每次只需要枚举出连通块,然后直接计算即可。
时间复杂度 (O(nsqrt n))
#pragma GCC optimze("O2")
#include <bits/stdc++.h>
using namespace std;
template <typename T>
void read(T& x) {
x = 0;
char ch = 0;
for (; !isdigit(ch); ch = getchar());
for (; isdigit(ch); ch = getchar()) {
x = x * 10 + (ch & 15);
}
}
const int N = 2e5 + 5;
struct edge {
int to, nxt;
} e[N << 1];
int head[N];
int edge_cnt;
int a[N];
vector<int> fac[N];
int vis[N];
long long ans[N];
int sz, dg;
void add_edge(int u, int v) {
e[++edge_cnt] = (edge){v, head[u]};
head[u] = edge_cnt;
}
void divide(int x, int id) {
for (int i = 1; i * i <= x; ++i) {
if (x % i == 0) {
fac[i].emplace_back(id);
if (i * i != x) {
fac[x / i].emplace_back(id);
}
}
}
}
void dfs(int u, int fa) {
++sz;
vis[u] = dg;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v != fa && a[v] % dg == 0) {
dfs(v, u);
}
}
}
int main() {
int n;
read(n);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
divide(a[i], i);
}
for (int i = 1; i < n; ++i) {
int u, v;
read(u), read(v);
add_edge(u, v);
add_edge(v, u);
}
for (dg = 1; dg <= 200000; ++dg) {
for (int i = 0, m = fac[dg].size(); i < m; ++i) {
if (vis[fac[dg][i]] != dg) {
sz = 0;
dfs(fac[dg][i], 0);
ans[dg] += (long long)sz * (sz - 1) / 2 + sz;
}
}
}
for (int i = 200000; i >= 1; --i) {
for (int j = i + i; j <= 200000; j += i) {
ans[i] -= ans[j];
}
}
for (int i = 1; i <= 200000; ++i) {
if (ans[i]) {
printf("%d %I64d
", i, ans[i]);
}
}
return 0;
}