題目描述
題目大意:給定一棵 個節點的樹,每個點有一個權值 ,保證 是一個 的排列。 求 其中, 是歐拉函數, 表示 兩個節點在樹上的距離。
題解
考慮到
具體證明的話可以分解質因數,然後就可以得到這個式子。
忽略掉 ,答案爲
然後我們可以枚舉 ,可以得到
設上述式子爲 。恰好 爲 不容易求,我們可以考慮求 是 的倍數的和,即
設上述式子爲 ,不難發現 ,根據莫比烏斯反演我們可以得到
因此我們考慮如何求 。由於 是個排列,所以對於每個 ,我們找出 是 的倍數的點,把 化成 。然後把乘法拆開,建立虛樹做 即可。
效率: (點數是 級別的,建立虛樹是 的)。
代碼
#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;
}