Codeforces809E Surprise me!

Problem

Codeforces
有一棵树,第 i i i 个点的点权为 a i a_i ai,保证 a i a_i ai 是一个排列,求

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_i*a_j)*dis(i,j) n(n1)1i=1nj=1nφ(aiaj)dis(i,j)

Solution

常数可以先丢掉,由

(1) φ ( a b ) = φ ( a ) φ ( b ) gcd ⁡ ( a , b ) φ ( gcd ⁡ ( a , b ) ) \varphi(ab)=\frac {\varphi(a)\varphi(b)\gcd(a,b)} {\varphi(\gcd(a,b))}\tag 1 φ(ab)=φ(gcd(a,b))φ(a)φ(b)gcd(a,b)(1)

可以把式子改成枚举 gcd ⁡ \gcd gcd 的形式

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

f ( d ) = d φ ( d ) ∑ i = 1 n ∑ j = 1 m [ ( a i , a j ) = 1 ] φ ( a i ) φ ( a j ) ∗ d i s ( i , j ) f(d)=\frac {d} {\varphi(d)}\sum_{i=1}^n\sum_{j=1}^m[(a_i,a_j)=1]\varphi(a_i)\varphi(a_j)*dis(i,j) f(d)=φ(d)di=1nj=1m[(ai,aj)=1]φ(ai)φ(aj)dis(i,j)

F ( d ) = d φ ( d ) ∑ i = 1 n ∑ j = 1 n [ d ∣ ( a i , a j ) ] φ ( a i ) φ ( a j ) ∗ d i s ( i , j ) F(d)=\frac {d} {\varphi(d)}\sum_{i=1}^n\sum_{j=1}^n[d|(a_i,a_j)]\varphi(a_i)\varphi(a_j)*dis(i,j) F(d)=φ(d)di=1nj=1n[d(ai,aj)]φ(ai)φ(aj)dis(i,j)

f ( d ) = ∑ d ∣ i μ ( i d ) F ( i ) f(d)=\sum_{d|i}\mu(\frac i d)F(i) f(d)=diμ(di)F(i)

暴力枚举 d d d,注意到总点数为 n ln ⁡ n n\ln n nlnn,那么可以通过在虚树上dfs的求出 F ( d ) F(d) F(d)

时间复杂度 O ( n ln ⁡ n log ⁡ n ) O(n\ln n\log n) O(nlnnlogn)。写起来还挺爽的。。

Code

#include <algorithm>
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=200010,mod=1e9+7;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
int n,ans,a[maxn],id[maxn],f[maxn],g[maxn];
namespace Math{
	int tot,pri[maxn],phi[maxn],mu[maxn];
	int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	int dec(int x,int y){return x<y?x-y+mod:x-y;}
	int power(int x,int y)
	{
		int res=1;
		for(;y;y>>=1,x=(ll)x*x%mod)
		  if(y&1)
		    res=(ll)res*x%mod;
		return res;
	}
	void init()
	{
		phi[1]=mu[1]=1;
		for(int i=2;i<=n;i++)
		{
			if(!phi[i]) phi[i]=i-1,mu[i]=mod-1,pri[++tot]=i;
			for(int j=1;j<=tot&&i*pri[j]<=n;j++)
			{
				if(i%pri[j]==0)
				{
					phi[i*pri[j]]=phi[i]*pri[j];
					mu[i*pri[j]]=0;
					break;
				}
				phi[i*pri[j]]=phi[i]*(pri[j]-1);
				mu[i*pri[j]]=dec(0,mu[i]);
			}
		}
	}
}
using Math::phi;
using Math::mu;
using Math::pls;
using Math::dec;
using Math::power;
namespace Tree{
	struct data{int v,nxt;}edge[maxn<<1];
	int p,dfc,head[maxn],dfn[maxn],f[maxn],dep[maxn],sz[maxn],hs[maxn],top[maxn];
	int cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
	void insert(int u,int v)
	{
		edge[++p]=(data){v,head[u]};head[u]=p;
		edge[++p]=(data){u,head[v]};head[v]=p;
	}
	void dfs1(int x)
	{
		sz[x]=1;dep[x]=dep[f[x]]+1;dfn[x]=++dfc;
		for(int i=head[x];i;i=edge[i].nxt)
		  if(edge[i].v^f[x])
		  {
		  	f[edge[i].v]=x;
			dfs1(edge[i].v);
			sz[x]+=sz[edge[i].v];
			if(sz[edge[i].v]>sz[hs[x]]) hs[x]=edge[i].v;
		  }
	}
	void dfs2(int x,int s)
	{
		top[x]=s;
		if(hs[x]) dfs2(hs[x],s);
		for(int i=head[x];i;i=edge[i].nxt)
		  if(edge[i].v^f[x]&&edge[i].v^hs[x])
		    dfs2(edge[i].v,edge[i].v);
	}
	int getlca(int x,int y)
	{
		int fx=top[x],fy=top[y];
		while(fx^fy)
		{
			if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
			x=f[fx];fx=top[x];
		}
		return dep[x]<dep[y]?x:y;
	}
}
using Tree::dep;
using Tree::insert;
using Tree::getlca;
using Tree::cmp;
namespace Virtual_Tree{
	struct data{int v,nxt;}edge[maxn<<1];
	int p,m,top,rt,sum,res,head[maxn],q[maxn],stk[maxn],tag[maxn];
	void insert(int u,int v)
	{
		edge[++p]=(data){v,head[u]};head[u]=p;
		edge[++p]=(data){u,head[v]};head[v]=p;
	}
	void build()
	{
		int t=0;top=0;
		sort(q+1,q+m+1,cmp);
		for(int i=1;i<=m;i++)
		{
			while(top)
			{
				t=getlca(q[i],stk[top]);
				if(top>1&&dep[t]<dep[stk[top-1]])
				  {insert(stk[top-1],stk[top]);--top;}
				else if(dep[t]<dep[stk[top]])
				  {insert(t,stk[top]);--top;break;}
				else break;
			}
			if(stk[top]^t){stk[++top]=t;if(!rt||dep[t]<dep[rt]) rt=t;}
			stk[++top]=q[i];tag[q[i]]=1;
			if(!rt||dep[q[i]]<dep[rt]) rt=t;
		}
		for(;top>1;--top) insert(stk[top-1],stk[top]);
	}
	int dfs(int x,int pre)
	{
		int sp=tag[x]?phi[a[x]]:0,sum=0,tmp;
		for(int i=head[x];i;i=edge[i].nxt)
		  if(edge[i].v^pre)
		  {
		  	tmp=dfs(edge[i].v,x);
		  	sum=(sum+(ll)sp*tmp)%mod;
		  	sp=pls(sp,tmp);
		  }
		head[x]=0;
		res=dec(res,4ll*dep[x]*sum%mod);
		return sp;
	}
	int work()
	{
		res=sum=rt=0;
		build();
		for(int i=1;i<=m;i++) sum=pls(sum,phi[a[q[i]]]);
		for(int i=1;i<=m;i++)
		  res=(res+(ll)phi[a[q[i]]]*dep[q[i]]%mod*dec(sum,phi[a[q[i]]]))%mod;
		sum=0;
		for(int i=1;i<=m;i++) sum=(sum+(ll)phi[a[q[i]]]*dep[q[i]])%mod;
		for(int i=1;i<=m;i++)
		  res=(res+(ll)phi[a[q[i]]]*dec(sum,(ll)phi[a[q[i]]]*dep[q[i]]%mod))%mod;
		dfs(rt,rt);
		p=0;
		for(int i=1;i<=m;i++) tag[q[i]]=0;
		return res;
	}
}
using Virtual_Tree::q;
using Virtual_Tree::m;
using Virtual_Tree::work;
void input()
{
	int u,v;read(n);
	for(int i=1;i<=n;i++){read(a[i]);id[a[i]]=i;}
	for(int i=1;i<n;i++){read(u);read(v);insert(u,v);}
	Math::init();
	Tree::dfs1(1);
	Tree::dfs2(1,1);
}
int main()
{
	input();
	for(int i=1;i<=n;i++)
	{
		m=0;
		for(int j=i;j<=n;j+=i) q[++m]=id[j];
		g[i]=work();
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i,r=1;j<=n;j+=i,++r)
		  f[i]=(f[i]+(ll)mu[r]*g[j])%mod;
		ans=(ans+(ll)f[i]*i%mod*power(phi[i],mod-2))%mod;
	}
	ans=(ll)ans*power((ll)n*(n-1)%mod,mod-2)%mod;
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值