/* 树上莫比乌斯反演 求树上 满足 d|gcd(au,av) gcd(au,av)的对数f(d) 如何求: 建立200000层新图,即对于每个数建立一个新图 在加边时,给gcd(au,av)的约数层的图的uv加边 f[i]表示第i层的满足条件 i | gcd(a[u],a[v]) 的对数,那么求一遍并查集,在合并过程中更新f[i]即可, 同时要注意f[i]初始值为这层的有效结点数量,对应i|gcd(a[u],a[u])这样的情况 然后用莫比乌斯反演来求最后答案g[d]=sigma(u[i]*f[i*d]) */ #include<bits/stdc++.h> using namespace std; #define maxn 200005 int phi[maxn],mu[maxn],prime[maxn],m; bool vis[maxn]; void init(){//打表mu phi[1]=mu[1]=1; for(int i=2;i<maxn;i++){ if(!vis[i]){ mu[i]=-1;prime[++m]=i;phi[i]=i-1; } for(int j=1;j<=m;j++){ if(i*prime[j]>=maxn)break; vis[i*prime[j]]=1; if(i%prime[j]==0){ phi[i*prime[j]]=phi[i]*prime[j]; mu[i*prime[j]]=0; break; } else phi[i*prime[j]]=phi[i]*(prime[j]-1),mu[i*prime[j]]=-mu[i]; } } } int n,a[maxn]; long long f[maxn],u[maxn],v[maxn]; vector<int>vec[maxn];//每层的边的下标集合 int F[maxn],size[maxn]; int find(int x){ return F[x]==x?x:F[x]=find(F[x]); } void bing(int i,int u,int v){ int t1=find(u),t2=find(v); if(t1==t2)return; f[i]+=(long long)size[t1]*size[t2]; size[t1]+=size[t2];F[t2]=t1; } int main(){ init(); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; for(int j=1;j*j<=a[i];j++) if(a[i]%j==0){ f[j]++; if(a[i]/j!=j)f[a[i]/j]++; } } for(int i=1;i<n;i++){//建立2000000层新图 scanf("%d%d",&u[i],&v[i]); int tmp=__gcd(a[u[i]],a[v[i]]); for(int j=1;j*j<=tmp;j++) if(tmp%j==0){ vec[j].push_back(i); if(tmp/j!=j) vec[tmp/j].push_back(i); } } //求并查集 for(int i=1;i<=200000;i++){ for(int j=0;j<vec[i].size();j++){//先初始化每层对应的并查集 int uu=u[vec[i][j]],vv=v[vec[i][j]]; F[uu]=uu;F[vv]=vv; size[uu]=1;size[vv]=1; } for(int j=0;j<vec[i].size();j++){//再进行合并求值 int uu=u[vec[i][j]],vv=v[vec[i][j]]; bing(i,uu,vv); } } //反演+输出 for(int d=1;d<=200000;d++){ long long ans=0; for(int i=1;i*d<=200000;i++) ans+=(long long)mu[i]*f[i*d]; if(ans!=0) cout<<d<<" "<<ans<<' '; } }