Codeforces809E - Surprise me!【歐拉函數變換 + 莫比烏斯反演 + 虛樹】

題目描述:

洛谷
在這裏插入圖片描述
n2105n\le2*10^5

題目分析:

首先考慮怎麼解決 φ(ij)\varphi(i*j)
我們希望的自然是把它變成 φ(i)φ(j)\varphi(i)*\varphi(j) 方便獨立計算,那麼觀察這兩個形式的差別:
φ(i)φ(j)=ijpi(11p)pj(11p)φ(ij)=ijpij(11p)\begin{aligned}\varphi(i)*\varphi(j)&=i*j*\prod_{p|i}(1-\frac 1p)\prod_{p|j}(1-\frac 1p)\\\varphi(i*j)&=i*j*\prod_{p|ij}(1-\frac 1p)\end{aligned}容易發現 φ(i)φ(j)\varphi(i)*\varphi(j) 多乘的是 gcd(i,j)gcd(i,j) 的質因子在φ\varphi中的形式,我們把它寫成 φ(gcd(i,j))gcd(i,j)\varphi(gcd(i,j))\over gcd(i,j)
於是 φ(ij)=φ(i)φ(j)gcd(i,j)φ(gcd(i,j))\varphi(i*j)={\varphi(i)\varphi(j)gcd(i,j)\over\varphi(gcd(i,j))}


回到原題目中,我們枚舉gcd(ai,aj)=dgcd(a_i,a_j)=d,那麼原式(去掉前面的1n(n1)\frac 1{n(n-1)})等於
d=1ndφ(d)i=1nj=1nφ(ai)φ(aj)[gcd(ai,aj)==d]dist(ai,aj)\sum_{d=1}^n{d\over \varphi(d)}\sum_{i=1}^n\sum_{j=1}^n\varphi(a_i)\varphi(a_j)[gcd(a_i,a_j)==d]dist(a_i,a_j)

f(d)=i=1nj=1nφ(ai)φ(aj)[gcd(ai,aj)==d]dist(ai,aj)f(d)=\sum_{i=1}^n\sum_{j=1}^n\varphi(a_i)\varphi(a_j)[gcd(a_i,a_j)==d]dist(a_i,a_j)

這個[gcd(ai,aj)==d][gcd(a_i,a_j)==d]的限制可以利用莫比烏斯反演轉化成計算所有gcdgcd等於dd的倍數的點對。
即計算F(d)=dxf(x)F(d)=\sum_{d|x}f(x)。而 F(d)=daidajφ(ai)φ(aj)dist(ai,aj)F(d)=\sum_{d|a_i}\sum_{d|a_j}\varphi(a_i)\varphi(a_j)dist(a_i,a_j)這個可以將dist(ai,aj)dist(a_i,a_j)拆開後用虛樹DP快速計算。因爲aia_i的權值是排列,所以總點數是O(nlnn)O(n\ln n)的。
然後 f(d)=dxF(x)μ(xd)f(d)=\sum_{d|x}F(x)*\mu(\frac xd)
最後再乘上1n(n1)\frac 1{n(n-1)}就可以了。總複雜度O(nlnn)O(n\ln n)

Code:

#include<bits/stdc++.h>
#define maxn 200005
using namespace std;
const int mod = 1e9+7;
int n,a[maxn],pos[maxn],q[maxn],sz,mu[maxn],phi[maxn],ans,F[maxn];
int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod); return s;}
void Pre(const int N){
	phi[1]=mu[1]=1; static int p[maxn],cnt=0; static bool v[maxn];
	for(int i=2;i<=N;i++){
		if(!v[i]) p[++cnt]=i,phi[i]=i-1,mu[i]=-1;
		for(int j=1,k;j<=cnt&&(k=i*p[j])<=N;j++){
			v[k]=1; if(i%p[j]==0) {phi[k]=phi[i]*p[j];break;} 
			phi[k]=phi[i]*(p[j]-1),mu[k]=-mu[i];
		}
	}
}
const int Log = 18;
int dfn[maxn],st[19][maxn*2],lg[maxn*2],dep[maxn],idx;
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
void dfs(int u,int ff){
	dep[u]=dep[ff]+1,st[0][++idx]=u,dfn[u]=idx;
	for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff)
		dfs(v,u),st[0][++idx]=u;
}
int LCA(int x,int y){
	if((x=dfn[x])>(y=dfn[y])) swap(x,y);
	int k=lg[y-x+1];
	return dep[st[k][x]]<dep[st[k][y-(1<<k)+1]]?st[k][x]:st[k][y-(1<<k)+1];
}
namespace VTree{
	int ret,k,sum[maxn],S[maxn],tp;
	bool kp[maxn];
	bool cmp(int i,int j){return dfn[i]<dfn[j];}
	void dfs2(int u){
		if(kp[u]) sum[u]=phi[a[u]],ret=(ret+1ll*phi[a[u]]*phi[a[u]]*dep[u])%mod;
		for(int i=fir[u],v;i;i=nxt[i])
			dfs2(v=to[i]),ret=(ret+2ll*sum[u]*sum[v]%mod*dep[u])%mod,sum[u]=(sum[u]+sum[v])%mod;
	}
	int solve(){
		sort(q+1,q+1+sz,cmp),k=sz,ret=0;
		for(int i=1;i<=k;i++) kp[q[i]]=1;
		for(int i=(S[tp=1]=q[1],2);i<=k;i++){
			int lca=LCA(S[tp],q[i]);
			for(;dfn[lca]<=dfn[S[tp-1]];tp--) line(S[tp-1],S[tp]);
			if(lca!=S[tp]) line(lca,S[tp]),S[tp]=q[++sz]=lca;
			S[++tp]=q[i];
		}
		for(;tp>1;tp--) line(S[tp-1],S[tp]);
		dfs2(S[1]),ret=-ret;
		for(int i=1;i<=k;i++) ret=(ret+1ll*phi[a[q[i]]]*dep[q[i]]%mod*sum[S[1]])%mod;
		for(tot=0;sz;sz--) fir[q[sz]]=sum[q[sz]]=kp[q[sz]]=0;
		return ret*2%mod;
	}
}
int main()
{
	scanf("%d",&n);
	Pre(n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),pos[a[i]]=i;
	for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
	dfs(1,0),lg[0]=-1;
	for(int i=1;i<=idx;i++) lg[i]=lg[i>>1]+1;
	for(int j=1;j<=Log;j++) for(int i=1,l=1<<j;i+l-1<=idx;i++) 
		st[j][i]=dep[st[j-1][i]]<dep[st[j-1][i+(l>>1)]]?st[j-1][i]:st[j-1][i+(l>>1)];
	memset(fir,0,sizeof fir),tot=0;
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j+=i) q[++sz]=pos[j];
		F[i]=VTree::solve();
	}
	for(int i=1;i<=n;i++){
		int now=0;
		for(int j=i;j<=n;j+=i) now=(now+F[j]*mu[j/i])%mod;
		ans=(ans+1ll*i*Pow(phi[i],mod-2)%mod*now)%mod;
	}
	printf("%d\n",((1ll*ans*Pow(1ll*n*(n-1)%mod,mod-2))%mod+mod)%mod);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章