LGP3790题解

建议改为 省 选 原 题

题意:求所有生成树的边权 \(\gcd\) 之和。

看到 \(\gcd\) 立刻想反演。

\[\sum_T\gcd_{e \in T}e_v \]

这里设 \(E=e_v(e \in T)\)

\[\sum_T\gcd_E \]

\[\sum_T\sum_{d \mid e(e \in E)}\varphi(d) \]

\[\sum_{d=1}^{\infty}\sum_{T,T \in E(e_v \in E(d \mid e_v))} \]

也就是说只需要求边权为 \(d\) 的倍数的边构成的图的生成树个数即可,使用矩阵树。

稍微剪枝一下,复杂度大概是 \(O(240mn^3+V\log V)\),实测可以通过。

#include<cstdio>
#include<vector>
typedef unsigned ui;
const ui M=1e6+5,mod=1e9+7;
ui n,m,mx,top,G[65][65],u[3005],v[3005],pos[M],pri[M],phi[M];std::vector<ui>id[M];
inline ui Add(const ui&a,const ui&b){
	return a+b>=mod?a+b-mod:a+b;
}
inline ui Del(const ui&a,const ui&b){
	return b>a?a-b+mod:a-b;
}
inline ui pow(ui a,ui b){
	ui ans=1;
	for(;b;b>>=1,a=1ull*a*a%mod)if(b&1)ans=1ull*ans*a%mod;
	return ans;
}
inline ui Gauss(){
	ui i,j,k,d,inv,ans(1);
	for(i=1;i^n;++i){
		if(!G[i][i])for(j=i+1;j^n;++j)if(G[j][i]){
			ans=mod-ans;std::swap(G[i],G[j]);break;
		}
		inv=pow(G[i][i],mod-2);ans=1ull*ans*G[i][i]%mod;
		for(j=i+1;j^n;++j)for(d=1ull*(mod-G[j][i])*inv%mod,k=i;k^n;++k)G[j][k]=(G[j][k]+1ull*d*G[i][k])%mod;
	}
	return ans;
}
inline ui Solve(const ui&x){
	ui i,j;
	for(i=1;i^n;++i)for(j=1;j^n;++j)G[i][j]=i^j?mod:0;
	for(i=1;(j=i*x)<=mx;++i)for(ui&x:id[j])++G[u[x]][u[x]],++G[v[x]][v[x]],--G[u[x]][v[x]],--G[v[x]][u[x]];
	return Gauss();
}
signed main(){
	ui i,j,x,S,val,ans;
	scanf("%u%u",&n,&m);
	for(i=1;i<=n;++i)for(j=1;j<=n;++j)G[i][j]=i^j?mod:0;
	for(i=1;i<=m;++i){
		scanf("%u%u%u",u+i,v+i,&val);id[val].push_back(i);if(val>mx)mx=val;
		++G[v[i]][v[i]];++G[u[i]][u[i]];--G[u[i]][v[i]];--G[v[i]][u[i]];
	}
	ans=Gauss();
	for(i=2;i<=mx;++i){
		if(!pos[i])pos[pri[++top]=i]=top,phi[i]=i-1;S=0;
		for(j=1;(x=i*j)<=mx;++j)S+=id[x].size();if(S>=n-1)ans=(ans+1ull*phi[i]*Solve(i))%mod;
		for(j=1;j<=pos[i]&&(x=i*pri[j])<=mx;++j)phi[x]=phi[i]*(pri[j]-((pos[x]=j)!=pos[i]));
	}
	printf("%u",ans);
}
posted @ 2022-01-11 14:57  Prean  阅读(27)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};