用在线LCA进行预处理,在对给出的两个点i,j算距离时,只要求出它们的最近公共祖先即可.
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <cmath>
#define maxn 10005
#define INF 1e9
typedef long long ll;
using namespace std;
struct Edge{
Edge(int a, int b){
from = a;
to = b;
}
int from, to;
};
vector<Edge> edge;
vector<int> v[maxn];
int d[maxn<<1], vis[maxn], num[maxn];
int dp[maxn<<1][55], kk[505][505];
int len;
void dfs(int j, int f, int h){
vis[j] = len;
d[len++] = h;
for(int i = 0; i < v[j].size(); i++){
Edge &e = edge[v[j][i]];
if(e.to != f){
dfs(e.to, e.from, h+1);
d[len++] = h;
}
}
}
void ST(){
for(int i = 0; i < len; i++)
dp[i][0] = d[i];
for(int j = 1; (1<<j) <= len; j++)
for(int i = 0; i+(1<<j) <= len; i++){
int a = dp[i][j-1], b = dp[i+(1<<(j-1))][j-1];
dp[i][j] = a < b ? a : b;
}
}
int gcd(int i, int j){
return j ? gcd(j, i % j) : i;
}
void Init(){
for(int i = 1; i <= 500; i++)
for(int j = i; j <= 500; j++){
if(gcd(i, j) == 1)
kk[j][i] = kk[i][j] = 1;
}
}
int main(){
// freopen("in.txt", "r", stdin);
int n;
Init();
while(scanf("%d", &n) == 1){
int a, b;
for(int i = 1; i <= n; i++)
scanf("%d", num+i);
for(int i = 1; i <= n; i++)
v[i].clear();
edge.clear();
for(int i = 1; i < n; i++){
scanf("%d%d", &a, &b);
edge.push_back(Edge(a, b));
edge.push_back(Edge(b, a));
int k = edge.size();
v[a].push_back(k-2);
v[b].push_back(k-1);
}
len = 0;
dfs(1, -1, 0);
ST();
int cnt = 0;
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++){
if(kk[num[i]][num[j]]){
int k1 = vis[i];
int k2 = vis[j];
if(k1 > k2)
swap(k1, k2);
int k = 0;
while(1<<(k+1) <= k2-k1+1)
k++;
int a = dp[k1][k], b = dp[k2-(1<<k)+1][k];
a = a < b ? a : b;
cnt += d[k1] + d[k2] - 2 * a;
}
}
printf("%d\n", cnt);
}
return 0;
}