CF809E Surprise me!

题目描述

题目大意:给定一棵 $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;
}

 

上一篇:P1050 循环 (欧拉定理解法)


下一篇:Jensen's inequality 及其应用