【学习笔记】CF809E Surprise me!

练习套路的一道题。

首先 ϕ ( i j ) \phi(ij) ϕ(ij)怎么拆?这里要用到一个不怎么常见的套路, ϕ ( i j ) = ϕ ( i ) ϕ ( j ) gcd ⁡ ( i , j ) ϕ ( gcd ⁡ ( i , j ) ) \phi(ij)=\frac{\phi(i)\phi(j)\gcd(i,j)}{\phi(\gcd(i,j))} ϕ(ij)=ϕ(gcd(i,j))ϕ(i)ϕ(j)gcd(i,j)

让我们观察这个式子: ∑ d = 1 n d ϕ ( d ) ∑ i = 1 n ∑ j = 1 n ϕ ( i ) ϕ ( j ) [ gcd ⁡ ( i , j ) = d ] dist(i,j) \sum_{d=1}^n\frac{d}{\phi(d)}\sum_{i=1}^n\sum_{j=1}^n\phi(i)\phi(j)[\gcd(i,j)=d]\text{dist(i,j)} d=1nϕ(d)di=1nj=1nϕ(i)ϕ(j)[gcd(i,j)=d]dist(i,j)

首先 ϕ ( i ) ϕ ( j ) \phi(i)\phi(j) ϕ(i)ϕ(j)可以看成两个点权之积,其次后面的艾佛森括号表示的是“恰好”的意思,那么我们可以想到将条件改写成 i , j i,j i,j均为 d d d的倍数。

考察: ∑ d ∣ i ∑ d ∣ j ϕ ( i ) ϕ ( j ) dist(i,j) \sum_{d|i}\sum_{d|j}\phi(i)\phi(j)\text{dist(i,j)} didjϕ(i)ϕ(j)dist(i,j) 这个式子不就可以在虚树上统计答案吗?

那么我们只需容斥即可。记 g ( d ) = ∑ d ∣ i ∑ d ∣ j ϕ ( i ) ϕ ( j ) dist(i,j) g(d)=\sum_{d|i}\sum_{d|j}\phi(i)\phi(j)\text{dist(i,j)} g(d)=didjϕ(i)ϕ(j)dist(i,j) f ( d ) = ∑ i = 1 n ∑ j = 1 n ϕ ( i ) ϕ ( j ) [ gcd ⁡ ( i , j ) = d ] dist(i,j) f(d)=\sum_{i=1}^n\sum_{j=1}^n\phi(i)\phi(j)[\gcd(i,j)=d]\text{dist(i,j)} f(d)=i=1nj=1nϕ(i)ϕ(j)[gcd(i,j)=d]dist(i,j)。那么 g ( d ) = ∑ d ∣ d ′ f ( d ′ ) g(d)=\sum_{d|d'}f(d') g(d)=ddf(d),显然可以得到 f ( d ) = ∑ d ∣ d ′ g ( d ′ ) μ ( d ′ d ) f(d)=\sum_{d|d'}g(d')\mu(\frac{d'}{d}) f(d)=ddg(d)μ(dd),因此只需计算 g ( d ) g(d) g(d)

只需把 d d d的倍数的点建虚树即可,这里不再赘述。

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

出题人是bot吧

#include<bits/stdc++.h>
#define pb push_back
#define ll long long
using namespace std;
const int mod=1e9+7;
const int N=200005;
int n,a[N],rk[N],dfn[N],son[N],fa[N],num,dep[N],siz[N],tp[N],dis[N];
ll f[N];
vector<int>g[N],g2[N];
ll fpow(ll x,ll y=mod-2){
	ll z(1);
	for(;y;y>>=1){
		if(y&1)z=z*x%mod;
		x=x*x%mod;
	}return z;
}
int Lca(int x,int y){
	int fx=tp[x],fy=tp[y];
	while(fx!=fy){
		if(dep[fx]>dep[fy])x=fa[fx];
		else y=fa[fy];
		fx=tp[x],fy=tp[y];
	}return dep[x]<dep[y]?x:y;
}
void dfs(int u,int topf){
	dfn[u]=++num,fa[u]=topf,siz[u]=1,dep[u]=dep[topf]+1;
	for(auto v:g[u]){
		if(v!=topf){
			dfs(v,u),siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])son[u]=v;
		}
	}
}
void dfs2(int u,int topf){
	tp[u]=topf;
	if(son[u])dfs2(son[u],topf);
	for(auto v:g[u]){
		if(!tp[v]){
			dfs2(v,v);
		}
	}
}
int prime[N],Cnt,mu[N],Vis[N],phi[N];
void init(int n){
	mu[1]=phi[1]=1;
	for(int i=2;i<=n;i++){
		if(!Vis[i])prime[++Cnt]=i,mu[i]=-1,phi[i]=i-1;
		for(int j=1;j<=Cnt&&prime[j]<=n/i;j++){
			Vis[i*prime[j]]=1;
			if(i%prime[j])mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*(prime[j]-1);
			else {
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
		}
	}
}
int p[N],vis[N],m,cnt,s[N];
vector<int>vec;
bool cmp(int x,int y){
	return dfn[x]<dfn[y];
}
ll S,sum[N],res;
void dfs3(int u,int id){
	sum[u]=0;if(vis[u])sum[u]=(sum[u]+phi[a[u]])%mod;
	for(auto v:g2[u]){
		dfs3(v,id);
		f[id]=(f[id]+2*sum[v]*(S-sum[v])%mod*dis[v]%mod)%mod,sum[u]=(sum[u]+sum[v])%mod;
	}
}
void add(int x,int y){
	if(dfn[x]>dfn[y])swap(x,y);
	g2[x].pb(y),dis[y]=dep[y]-dep[x];
}
int gcd(int x,int y){
	return y==0?x:gcd(y,x%y);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n,init(n);for(int i=1;i<=n;i++)cin>>a[i],rk[a[i]]=i;
	for(int i=1;i<n;i++){
		int x,y;cin>>x>>y,g[x].pb(y),g[y].pb(x);
	}dfs(1,0),dfs2(1,1);
	for(int i=1;i<=n;i++){
		m=S=0;
		for(int j=i;j<=n;j+=i)p[++m]=rk[j],S=(S+phi[j])%mod,vis[rk[j]]=1,vec.pb(rk[j]);
		s[cnt=1]=1,vec.pb(1);
		sort(p+1,p+1+m,cmp);
		for(int i=1;i<=m;i++){
			if(p[i]==1)continue;
			int u=p[i],v=Lca(u,s[cnt]);
			if(s[cnt]==v)s[++cnt]=u;
			else{
				int lst=0;
				while(dep[s[cnt]]>dep[v]){
					if(lst)add(s[cnt],lst);
					lst=s[cnt--];
				}
				if(s[cnt]==v){
					add(s[cnt],lst),s[++cnt]=u;
				}
				else{
					add(v,lst),s[++cnt]=v,vec.pb(v),s[++cnt]=u;
				}
			}
		}for(int i=1;i<cnt;i++)add(s[i],s[i+1]);
		dfs3(1,i);
		for(int j=0;j<vec.size();j++)vis[vec[j]]=0,g2[vec[j]].clear();vec.clear();
	}
	for(int i=1;i<=n;i++){
		ll tot=0;
		for(int j=i;j<=n;j+=i){
			tot=(tot+mu[j/i]*f[j]%mod)%mod;
		}
		res=(res+tot*i%mod*fpow(phi[i])%mod)%mod;
	}
	cout<<(res+mod)%mod*fpow(n)%mod*fpow(n-1)%mod;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值