「Codeforces 809E」Surprise me!

传送门


problem

给出一个 n n n 个点的树,每个点有一个点权 a i a_i ai,且 a i a_i ai 构成 1 ∼ n 1\sim n 1n 的一个排列,求:

1 n ( n − 1 ) ∑ i = 1 n ∑ j = 1 n φ ( a i a j ) × d i s ( i , j ) \frac 1{n(n-1)}\sum_{i=1}^n\sum_{j=1}^n\varphi(a_ia_j)\times \mathrm{dis}(i,j) n(n1)1i=1nj=1nφ(aiaj)×dis(i,j)

其中 φ \varphi φ 是欧拉函数, d i s ( i , j ) dis(i,j) dis(i,j) 表示 i , j i,j i,j 两点在树上的距离。答案对 1 0 9 + 7 10^9+7 109+7 取模。

数据范围: 2   ≤   n   ≤   2 × 1 0 5 2 ≤ n ≤ 2\times10^5 2n2×105


solution

这也是道神题啊 orz。

首先,欧拉函数有一个性质:

φ ( i j ) = φ ( i ) φ ( j ) gcd ⁡ ( i , j ) φ ( gcd ⁡ ( i , j ) ) \varphi(ij)=\varphi(i)\varphi(j)\frac{\gcd(i,j)}{\varphi(\gcd(i,j))} φ(ij)=φ(i)φ(j)φ(gcd(i,j))gcd(i,j)

这个根据 φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p k ) \varphi(n)=n(1-\frac 1 {p_1})(1-\frac 1{p_2})\cdots(1-\frac 1{p_k}) φ(n)=n(1p11)(1p21)(1pk1) 即可推出。

先把 1 n ( n − 1 ) \frac 1{n(n-1)} n(n1)1 抛开,考虑求:

a n s = ∑ i = 1 n ∑ j = 1 n φ ( a i ) φ ( a j ) gcd ⁡ ( a i , a j ) φ ( gcd ⁡ ( a i , a j ) ) × d i s ( i , j ) ans=\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))}\times \mathrm{dis}(i,j) ans=i=1nj=1nφ(ai)φ(aj)φ(gcd(ai,aj))gcd(ai,aj)×dis(i,j)

枚举 gcd ⁡ \gcd gcd,得到:

a n s = ∑ k = 1 n k φ ( k ) ∑ i = 1 n ∑ j = 1 n [ gcd ⁡ ( a i , a j ) = k ] φ ( a i ) φ ( a j ) × d i s ( i , j ) ans=\sum_{k=1}^n\frac{k}{\varphi(k)}\sum_{i=1}^n\sum_{j=1}^n[\gcd(a_i,a_j)=k]\varphi(a_i)\varphi(a_j)\times \mathrm{dis}(i,j) ans=k=1nφ(k)ki=1nj=1n[gcd(ai,aj)=k]φ(ai)φ(aj)×dis(i,j)

我们做一个小转化,设值 i i i 对应的点为 p i p_i pi,则:

a n s = ∑ k = 1 n k φ ( k ) ∑ i = 1 n ∑ j = 1 n [ gcd ⁡ ( i , j ) = k ] φ ( i ) φ ( j ) × d i s ( p i , p j ) = ∑ k = 1 n k φ ( k ) ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ n k ⌋ [ gcd ⁡ ( i , j ) = 1 ] φ ( i k ) φ ( j k ) × d i s ( p i k , p j k ) \begin{aligned} ans&=\sum_{k=1}^n\frac{k}{\varphi(k)}\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=k]\varphi(i)\varphi(j)\times \mathrm{dis}(p_i,p_j)\\ &=\sum_{k=1}^n\frac{k}{\varphi(k)}\sum_{i=1}^{\lfloor\frac nk\rfloor}\sum_{j=1}^{\lfloor\frac nk\rfloor}[\gcd(i,j)=1]\varphi(ik)\varphi(jk)\times \mathrm{dis}(p_{ik},p_{jk}) \end{aligned} ans=k=1nφ(k)ki=1nj=1n[gcd(i,j)=k]φ(i)φ(j)×dis(pi,pj)=k=1nφ(k)ki=1knj=1kn[gcd(i,j)=1]φ(ik)φ(jk)×dis(pik,pjk)

然后就可以莫反了:

a n s = ∑ k = 1 n k φ ( k ) ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ n k ⌋ ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) φ ( i k ) φ ( j k ) × d i s ( p i k , p j k ) = ∑ k = 1 n k φ ( k ) ∑ d = 1 ⌊ n k ⌋ μ ( d ) ∑ i = 1 ⌊ n k d ⌋ ∑ j = 1 ⌊ n k d ⌋ φ ( i k d ) φ ( j k d ) × d i s ( p i k d , p j k d ) \begin{aligned} ans&=\sum_{k=1}^n\frac{k}{\varphi(k)}\sum_{i=1}^{\lfloor\frac nk\rfloor}\sum_{j=1}^{\lfloor\frac nk\rfloor}\sum_{d|\gcd(i,j)}\mu(d)\varphi(ik)\varphi(jk)\times \mathrm{dis}(p_{ik},p_{jk})\\ &=\sum_{k=1}^n\frac{k}{\varphi(k)}\sum_{d=1}^{{\lfloor\frac nk\rfloor}}\mu(d)\sum_{i=1}^{\lfloor\frac n{kd}\rfloor}\sum_{j=1}^{\lfloor\frac n{kd}\rfloor}\varphi(ikd)\varphi(jkd)\times \mathrm{dis}(p_{ikd},p_{jkd}) \end{aligned} ans=k=1nφ(k)ki=1knj=1kndgcd(i,j)μ(d)φ(ik)φ(jk)×dis(pik,pjk)=k=1nφ(k)kd=1knμ(d)i=1kdnj=1kdnφ(ikd)φ(jkd)×dis(pikd,pjkd)

T = k d T=kd T=kd,我们枚举 T T T,得:

a n s = ∑ T = 1 n ∑ k ∣ T μ ( T k ) k φ ( k ) ∑ i = 1 ⌊ n T ⌋ ∑ j = 1 ⌊ n T ⌋ φ ( i T ) φ ( j T ) × d i s ( p i T , p j T ) ans=\sum_{T=1}^n\sum_{k|T}\frac{\mu(\frac Tk)k}{\varphi(k)}\sum_{i=1}^{\lfloor\frac n T\rfloor}\sum_{j=1}^{\lfloor\frac n T\rfloor}\varphi(iT)\varphi(jT)\times\mathrm{dis}(p_{iT},p_{jT}) ans=T=1nkTφ(k)μ(kT)ki=1Tnj=1Tnφ(iT)φ(jT)×dis(piT,pjT)

f ( n ) = ∑ k ∣ n μ ( n k ) k φ ( k ) f(n)=\sum_{k|n}\frac{\mu(\frac n k)k}{\varphi(k)} f(n)=knφ(k)μ(kn)k,这个可以枚举倍数在 O ( n log ⁡ n ) O(n\log n) O(nlogn) 的时间内算出来。

考虑怎么计算后面的那个式子,由于每次涉及的点只有 O ( ⌊ n T ⌋ ) O(\lfloor\frac n T\rfloor) O(Tn) 个,就想到可以在虚树 d p dp dp

具体就是,把虚树建出来之后,考虑一下每条边的贡献就可以了。

PS:注意最后别忘了除以 n ( n − 1 ) n(n-1) n(n1)

PPS:其实感觉这道题莫反挺套路的,关键是要想到那个转化在虚树上 d p dp dp

时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)


code

#include<bits/stdc++.h>
#define N 200005
#define P 1000000007
using namespace std;
int n,tot;
int a[N],pos[N],dfn[N],dep[N],fa[N][21];
struct edge{
	int t,first[N],v[N<<1],nxt[N<<1];
	void add(int x,int y){
		nxt[++t]=first[x],first[x]=t,v[t]=y;
	}
}T,VT;
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
int inv(int x)  {return power(x,P-2);}
bool comp(const int &p,const int &q)  {return dfn[p]<dfn[q];}
int sum,prime[N],mark[N],mu[N],phi[N],f[N];
void linear_sieves(){
	phi[1]=mu[1]=1;
	for(int i=2;i<N;++i){
		if(!mark[i])  prime[++sum]=i,mu[i]=P-1,phi[i]=i-1;
		for(int j=1;j<=sum&&i*prime[j]<N;++j){
			mark[i*prime[j]]=1;
			if(i%prime[j])  mu[i*prime[j]]=P-mu[i],phi[i*prime[j]]=phi[i]*(prime[j]-1);
			else  {mu[i*prime[j]]=0,phi[i*prime[j]]=phi[i]*prime[j];break;}
		}
	}
	for(int i=1;i<N;++i){
		int Inv=inv(phi[i]);
		for(int j=1;i*j<N;++j)
			f[i*j]=add(f[i*j],mul(mul(i,mu[j]),Inv));
	}
}
void dfs(int x){
	dfn[x]=++tot;
	for(int i=1;i<=20;++i)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=T.first[x];i;i=T.nxt[i]){
		int to=T.v[i];
		if(to==fa[x][0])  continue;
		fa[to][0]=x,dep[to]=dep[x]+1;
		dfs(to);
	}
}
int LCA(int x,int y){
	if(dep[x]<dep[y])  swap(x,y);
	for(int i=20;~i;--i)  if(dep[fa[x][i]]>=dep[y])  x=fa[x][i];
	if(x==y)  return x;
	for(int i=20;~i;--i)  if(fa[x][i]!=fa[y][i])  x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}
namespace Virtual_Tree{
	int sum,res,cnt,top;
	int p[N],S[N],stk[N];
	void Insert(int x){
		if(top==1)  {stk[++top]=x;return;}
		int lca=LCA(x,stk[top]);
		if(lca==stk[top])  {stk[++top]=x;return;}
		while(top>1&&dfn[lca]<=dfn[stk[top-1]])
			VT.add(stk[top-1],stk[top]),top--;
		if(lca!=stk[top])  VT.add(lca,stk[top]),stk[top]=lca;
		stk[++top]=x;
	}
	int dis(int x,int y)  {return dep[x]+dep[y]-2*dep[LCA(x,y)];}
	void dp(int x){
		for(int i=VT.first[x];i;i=VT.nxt[i]){
			int to=VT.v[i],len=dis(x,to);
			dp(to);
			res=add(res,mul(len,mul(dec(sum,S[to]),S[to])));
			S[x]=add(S[x],S[to]);
		}
	}
	void Clear(int x){
		for(int i=VT.first[x];i;i=VT.nxt[i])  Clear(VT.v[i]);
		S[x]=VT.first[x]=0;
	}
	int calc(int x){
		VT.t=cnt=sum=res=0;
		for(int i=1;i*x<=n;++i)  p[++cnt]=pos[i*x],S[p[cnt]]=phi[i*x],sum=add(sum,phi[i*x]);
		sort(p+1,p+cnt+1,comp),stk[top=1]=1;
		for(int i=1;i<=cnt;++i)  if(p[i]!=1)  Insert(p[i]);
		while(--top)  VT.add(stk[top],stk[top+1]);
		dp(1),Clear(1);
		res=mul(add(res,res),f[x]);
		return res;
	}
}
using namespace Virtual_Tree;
int main(){
	scanf("%d",&n),linear_sieves();
	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);
		T.add(x,y),T.add(y,x);
	}
	dep[1]=1,dfs(1);
	int ans=0;
	for(int i=1;i<=n;++i)  ans=add(ans,calc(i));
	printf("%d\n",mul(ans,inv(mul(n,n-1))));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值