CF809E Surprise me!

題目描述

題目大意:給定一棵 nn 個節點的樹,每個點有一個權值 aia_i ,保證 aia_i 是一個 1...n1...n 的排列。 求 1n(n1)i=1nj=1nφ(aiaj)dist(i,j)\frac{1}{n(n-1)}\sum_{i=1}^n\sum_{j=1}^n\varphi(a_ia_j)dist(i,j) 其中, φ(x)\varphi(x) 是歐拉函數, dist(i,j)dist(i,j) 表示 i,ji,j 兩個節點在樹上的距離。

題解

考慮到 φ(ij)=φ(i)φ(j)gcd(i,j)φ(gcd(i,j))\varphi(ij)= \varphi(i) \varphi(j) \frac{gcd(i,j)}{\varphi(gcd(i,j))}

具體證明的話可以分解質因數,然後就可以得到這個式子。

忽略掉 1n(n1)\frac{1}{n(n-1)} ,答案爲 i=1nj=1nφ(ai)φ(aj)gcd(ai,aj)φ(gcd(ai,aj))dist(i,j)\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)

然後我們可以枚舉 gcdgcd ,可以得到 d=1ndφ(d)i=1nj=1n[d=gcd(ai,aj)]φ(ai)φ(aj)dist(i,j)\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)g(d) 。恰好 gcdgcddd 不容易求,我們可以考慮求 gcdgcddd 的倍數的和,即 d=1ndφ(d)i=1nj=1n[dgcd(ai,aj)]φ(ai)φ(aj)dist(i,j)\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) ,不難發現 f(d)=dig(i)f(d)=\sum_{d|i}g(i) ,根據莫比烏斯反演我們可以得到 g(d)=dif(i)μ(id)g(d)=\sum_{d|i}f(i)\mu(\frac{i}{d})

因此我們考慮如何求 f(d)f(d) 。由於 aia_i 是個排列,所以對於每個 dd ,我們找出 aia_idd 的倍數的點,把 dist(i,j)dist(i,j) 化成 deep[i]+deep[j]2deep[lca(i,j)]deep[i]+deep[j]-2deep[lca(i,j)] 。然後把乘法拆開,建立虛樹做 dp\text{dp} 即可。

效率: O(nlog2n)O(nlog^2n) (點數是 O(nlogn)O(nlogn) 級別的,建立虛樹是 O(logn)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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章