题目大意:给定一棵 $n$ 个节点的树,每个点有一个权值 $a_i$ ,保证 $a_i$ 是一个 $1...n$ 的排列。 求 $$\frac{1}{n(n-1)}\sum_{i=1}^n\sum_{j=1}^n\varphi(a_ia_j)dist(i,j)$$ 其中, $\varphi(x)$ 是欧拉函数, $dist(i,j)$ 表示 $i,j$ 两个节点在树上的距离。
题解
考虑到 $$\varphi(ij)= \varphi(i) \varphi(j) \frac{gcd(i,j)}{\varphi(gcd(i,j))}$$
具体证明的话可以分解质因数,然后就可以得到这个式子。
忽略掉 $\frac{1}{n(n-1)}$ ,答案为 $$\sum_{i=1}^n\sum_{j=1}^n \varphi(a_i) \varphi(a_j) \frac{gcd(a_i,a_j)}{\varphi(gcd(a_i,a_j))}dist(i,j)$$
然后我们可以枚举 $gcd$ ,可以得到 $$\sum_{d=1}^n\frac{d}{\varphi(d)}\sum_{i=1}^n\sum_{j=1}^n [d=gcd(a_i,a_j)]\varphi(a_i) \varphi(a_j) dist(i,j)$$
设上述式子为 $g(d)$ 。恰好 $gcd$ 为 $d$ 不容易求,我们可以考虑求 $gcd$ 是 $d$ 的倍数的和,即 $$\sum_{d=1}^n\frac{d}{\varphi(d)}\sum_{i=1}^n\sum_{j=1}^n [d|gcd(a_i,a_j)]\varphi(a_i) \varphi(a_j) dist(i,j)$$
设上述式子为 $f(d)$ ,不难发现 $f(d)=\sum_{d|i}g(i)$ ,根据莫比乌斯反演我们可以得到 $g(d)=\sum_{d|i}f(i)\mu(\frac{i}{d})$
因此我们考虑如何求 $f(d)$ 。由于 $a_i$ 是个排列,所以对于每个 $d$ ,我们找出 $a_i$ 是 $d$ 的倍数的点,把 $dist(i,j)$ 化成 $deep[i]+deep[j]-2deep[lca(i,j)]$ 。然后把乘法拆开,建立虚树做 $\text{dp}$ 即可。
效率: $O(nlog^2n)$ (点数是 $O(nlogn)$ 级别的,建立虚树是 $O(logn)$ 的)。
代码
#include <bits/stdc++.h> using namespace std; const int N=2e5+5,M=N<<1,P=1e9+7; int n,a[N],hd[N],V[M],nx[M],t,b[N],pr[N],mu[N],fi[N],e[N],c,id[N]; int f[21][M],Lg[M],p[N],S[N],tp,s1,s2,d,ans,h[N],F[N],G[N],dp[N]; bool vis[N];vector<int>g[N]; int K(int x,int y){ int z=1; for (;y;y>>=1,x=1ll*x*x%P) if (y&1) z=1ll*z*x%P; return z; } void add(int u,int v){ nx[++t]=hd[u];V[hd[u]=t]=v; } void Add(int u,int v){ g[u].push_back(v); } void dfs(int u,int fr){ f[0][e[u]=++c]=u;id[u]=++t;dp[u]=dp[fr]+1; for (int i=hd[u];i;i=nx[i]) if (V[i]!=fr) dfs(V[i],u),f[0][++c]=u; } int Min(int u,int v){return dp[u]<dp[v]?u:v;} int lca(int u,int v){ u=e[u];v=e[v]; if (u>v) swap(u,v); int i=Lg[v-u+1]; return Min(f[i][u],f[i][v-(1<<i)+1]); } bool cmp(int x,int y){return id[x]<id[y];} void ins(int x){ if (!tp){S[++tp]=x;return;} int u=lca(S[tp],x); if (u==S[tp]){S[++tp]=x;return;} while(tp>1 && id[u]<=id[S[tp-1]]) Add(S[tp-1],S[tp]),tp--; if (u!=S[tp]) Add(u,S[tp]),S[tp]=u; S[++tp]=x; } void dfs(int u){ h[u]=0;int z=g[u].size(); if (a[u]%d==0) (F[d]+=P-1ll*fi[a[u]]*fi[a[u]]%P*dp[u]%P)%=P, h[u]+=fi[a[u]]; for (int v,i=0;i<z;i++) dfs(v=g[u][i]), (F[d]+=P-2ll*h[u]*h[v]%P*dp[u]%P)%=P, (h[u]+=h[v])%=P; g[u].clear(); } void work(){ t=tp=s1=s2=0; for (int i=d;i<=n;i+=d) p[++t]=b[i],(s1+=fi[i])%=P, (s2+=1ll*fi[i]*dp[b[i]]%P)%=P; sort(p+1,p+t+1,cmp); if (p[1]!=1) ins(1); for (int i=1;i<=t;i++) ins(p[i]); while(tp>1) Add(S[tp-1],S[tp]),tp--; dfs(1);(F[d]+=(F[d]+2ll*s1*s2%P)%P)%=P; } int main(){ cin>>n;fi[1]=mu[1]=1; for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[a[i]]=i; for (int i=2;i<=n;i++){ if (!vis[i]) pr[++t]=i,mu[i]=P-1,fi[i]=i-1; for (int v,j=1;j<=t;j++){ v=pr[j]*i;if (v>n) break;vis[v]=1; if (i%pr[j]) fi[v]=fi[i]*(pr[j]-1),mu[v]=P-mu[i]; else{mu[v]=0;fi[v]=fi[i]*pr[j];break;} } } t=0; for (int x,y,i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,t=0); for (int i=2;i<=c;i++) Lg[i]=Lg[i>>1]+1; for (int i=c;i;i--) for (int j=1;i+(1<<j)<=c+1;j++) f[j][i]=Min(f[j-1][i],f[j-1][i+(1<<(j-1))]); for (d=1;d<=n;d++) work(); for (int i=1;i<=n;i++) for (int j=i;j<=n;j+=i) (G[i]+=1ll*mu[j/i]*F[j]%P)%=P; for (int i=1;i<=n;i++) (ans+=1ll*i*K(fi[i],P-2)%P*G[i]%P)%=P; cout<<1ll*ans*K(1ll*n*(n-1)%P,P-2)%P<<endl;return 0; }