\(\text{Solution:}\)
考慮一個 naive 的想法,首先直接枚舉答案 \(i,\) 然後把所有是 \(i\) 的倍數的點全部拉出來,這樣它們就會組成一些連通塊。
依次統計其路徑條數,那麼這些就是 \(i|\gcd\) 的答案。
那麼設 \(f(i)\) 表示我們算出來的答案,設 \(F(i)\) 表示我們想要的答案。
那麼此時顯然有 \(f(i)=\sum_{i|d} F(d)\)
直接考慮一波反演,\(F(i)=\sum_{i|d}\mu(\frac{d}{i})f(d)\) 即可。
這裏有一些學習到的小技巧以及注意事項:
首先是如何把所有點拉出來形成連通塊:直接維護點的話發現再枚舉連接它的邊複雜度就炸了,所以考慮直接找邊。對於一條邊,求其端點的 \(\gcd,\) 然後暴力找其因數記錄編號即可。
連邊可以用並查集維護一下 \(siz\)
然後注意莫反的時候要用到所有是它倍數的點的答案,所以不要因爲這個點的實際答案是 \(0\) 就直接不去計算,這樣會導致反演出錯
還有,注意到 \(256M\) 的空間一定要留意,這題一旦 #define int long long
就直接炸空間了
#include<bits/stdc++.h>
using namespace std;
typedef double db;
//#define int long long
const int mod=1e9+7;
const db eps=1e-10;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
inline db Max(db x,db y){return x-y>eps?x:y;}
inline db Min(db x,db y){return x-y<eps?x:y;}
inline int Add(int x,int y,int M=mod){return (x+y)%M;}
inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
typedef pair<int,int> pii;
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
#define bc __builtin_popcount
#define pc putchar
#define ci const int&
inline int Abs(int x){return x<0?-x:x;}
//char buf[1<<21],*p1=buf,*p2=buf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char Obuf[1<<21],*O=Obuf;//Siz shoule be the size of Out File
//int pst[30],ptop;
//inline void Fwrite(int x){
// if(x<0)*O++='-',x=-x;ptop=0;
// while(x)pst[++ptop]=x%10,x/=10;
// while(ptop)*O++=pst[ptop--]+'0';
//}
//inline void Fprint(){fwrite(Obuf,1,O-Obuf,stdout);}
inline int read(){
int s=0,w=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
inline void write(int x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline int qpow(int x,int y){
int res=1;
while(y){
if(y&1)res=Mul(res,x);
x=Mul(x,x);y>>=1;
}
return res;
}
inline void cadd(int &x,int y){x+=y;}
inline void cmul(int &x,int y){x*=y;}
inline void cmax(int &x,int y){x=Max(x,y);}
inline void cmin(int &x,int y){x=Min(x,y);}
const int N=2e5+10;
namespace Refined_heart{
int a[N],n,siz[N],vvis[N],pr[N],cntt,mu[N];
long long g[N];
struct E{int u,v;}edge[N];
inline void pred(){
mu[1]=1;
for(int i=2;i<=200000;++i){
if(!vvis[i])pr[++cntt]=i,mu[i]=-1;
for(int j=1;j<=cntt&&pr[j]*i<=200000;++j){
vvis[i*pr[j]]=1;
if(i%pr[j]==0)break;
mu[i*pr[j]]=-mu[i];
}
}
}
inline int gcd(int x,int y){return !y?x:gcd(y,x%y);}
poly d[N];
int node[N],rec[N],rcnt;
int f[N],vis[N],V[N],ans[N];
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
inline void merge(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx==fy)return;
f[fx]=fy;siz[fy]+=siz[fx];
}
void solve(){
n=read();
for(int i=1;i<=n;++i)a[i]=read(),node[a[i]]++,siz[i]=1,f[i]=i;
for(int i=1;i<n;++i){
int u=read(),v=read();
edge[i]=(E){u,v};
int gd=gcd(a[u],a[v]);
for(int j=1;j*j<=gd;++j){
if(gd%j)continue;
d[j].pb(i);
if(j*j!=gd)d[gd/j].pb(i);
}
}
pred();
for(int i=200000;i;--i){
int cnt=0;
int ts=0;
for(auto pos:d[i]){
merge(edge[pos].u,edge[pos].v);
++cnt;
if(!vis[edge[pos].u]){
rec[++rcnt]=edge[pos].u;
vis[edge[pos].u]=1;
++ts;
}
if(!vis[edge[pos].v]){
rec[++rcnt]=edge[pos].v;
vis[edge[pos].v]=1;
++ts;
}
}
long long ct=0;
for(int now=i;now<=200000;now+=i)ct+=node[now];
g[i]+=ct-ts;
for(int j=1;j<=rcnt;++j){
int v=rec[j];
int fv=find(v);
if(V[fv])continue;
V[fv]=1;
if(siz[fv]==1)++g[i];
else{
long long c=1ll*siz[fv]*siz[fv];
c+=siz[fv];
c/=2;
g[i]+=c;
}
}
for(int j=1;j<=rcnt;++j){
int v=rec[j];
vis[v]=0;
int fv=find(v);
V[fv]=0;
siz[v]=1;
f[v]=v;
}
rcnt=0;
}
for(int i=1;i<=200000;++i){
long long aans=0;
for(int k=1;i*k<=200000;++k)aans+=1ll*mu[k]*g[i*k];
if(aans)printf("%d %lld\n",i,aans);
}
}
}
signed main(){
freopen("in.txt","r",stdin);
Refined_heart::solve();
return 0;
}