[2020 年聯考 A 卷] 作業題 (莫比烏斯反演+變元矩陣樹定理)

題面:https://www.luogu.com.cn/problem/P6624

 

 

題解

一道套路題

先來考慮gcd=1的情況

如何求所有生成樹的邊權和?

使用變元矩陣樹,把每一條邊的邊權賦爲1+wx(w爲它原來的邊權)

然後求其在mod x^2意義下的答案

那麼x項的係數就是所有生成樹的邊權和

因爲要得到x項,只能有一條邊貢獻出自己的邊權,其它的邊都只能貢獻1

所以這樣做是可以求出生成樹的邊權和的

我們可以對矩陣上的數維護二元組

如何求逆元呢?(a+bx)*(a-bx)=a^2-b^2*x^2

那麼兩邊除一個a^2

(a+bx)*\frac{a-bx}{a^2}=1-\frac{b^2}{a^2}*x^2=1 (mod x^2)

所以a+bx的逆元爲\frac{a-bx}{a^2}

其它的加減乘法稍微重載一下就好了

 

再來考慮gcd不一定爲1的情況

我們設f(n)表示邊權gcd恰好爲n時的生成樹的邊權和,這個不好直接統計

設g(n)表示邊權gcd爲n的倍數的生成樹的邊權和

於是有 g(n)=\sum_{n|d}f(d)

那麼f(n)=\sum_{n|d}g(d)*\mu(\frac{d}{n})

最後的答案就是∑i*f(i)(算上gcd的貢獻)

就做完了

 

代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define N 35
const int mod=998244353;
int ksm(int x,int y)
{
	int ret=1;
	while(y){
		if(y&1)ret=1ll*ret*x%mod;
		y>>=1;x=1ll*x*x%mod;
	}
	return ret;
}
struct P{
	int x,y;
	P(){x=y=0;}
	P(int _x,int _y){x=_x;y=_y;}
	P operator + (const P &t)const{return P((x+t.x)%mod,(y+t.y)%mod);}
	P operator - (const P &t)const{return P((x+mod-t.x)%mod,(y+mod-t.y)%mod);}
	P operator * (const P &t)const{return P(1ll*x*t.x%mod,(1ll*x*t.y+1ll*y*t.x)%mod);}
}a[N][N];
P inv(P s){
	int ni=ksm(1ll*s.x*s.x%mod,mod-2);
	return P(1ll*s.x*ni%mod,1ll*(mod-s.y)*ni%mod);
}
P det(int n)
{
	int i,j,k;P ans=P(1,0);
	for(i=1;i<=n;i++){
		if(!a[i][i].x){
			for(j=i+1;j<=n;j++)
				if(a[j][i].x)break;
			if(j>n)return P(0,0);
			for(k=i;k<=n;k++)swap(a[i][k],a[j][k]);
		}
		for(j=i+1;j<=n;j++){
			P w=inv(a[i][i])*a[j][i];
			for(k=i;k<=n;k++)
				a[j][k]=a[j][k]-a[i][k]*w;
		}
	}
	for(i=1;i<=n;i++)ans=ans*a[i][i];
	return ans;
}
#define M 200005
int prime[M],tot,mu[M];
bool vis[M];
void shai()
{
	int i,j,n=200000;
	vis[1]=1;mu[1]=1;
	for(i=2;i<=n;i++){
		if(!vis[i]){
			prime[++tot]=i;
			mu[i]=-1;
		}
		for(j=1;j<=tot;j++){
			int tmp=i*prime[j];
			if(tmp>n)break;
			vis[tmp]=1;
			if(i%prime[j]==0){mu[tmp]=0;break;}
			mu[tmp]=(mod-mu[i])%mod;
		}
	}
}
struct node{int u,v,w;}tmp;
vector<node> e[M];
int f[M],g[M];
int main()
{
	//freopen("count.in","r",stdin);
	//freopen("count.out","w",stdout);
	int n,m,i,j,k,u,v,w,mx=0;shai();
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++){
		scanf("%d%d%d",&u,&v,&w);
		mx=max(mx,w);tmp.u=u;tmp.v=v;tmp.w=w;
		e[w].push_back(tmp);
	}
	for(i=1;i<=mx;i++){
		memset(a,0,sizeof(a));
		int cnt=0;
		for(j=i;j<=mx;j+=i){
			for(k=0;k<(int)e[j].size();k++){
				u=e[j][k].u;v=e[j][k].v;w=e[j][k].w;
				a[u][v]=P(mod-1,mod-w);
				a[v][u]=P(mod-1,mod-w);
				a[u][u]=a[u][u]+P(1,w);
				a[v][v]=a[v][v]+P(1,w);
				cnt++;
			}
		}
		if(cnt<n-1)continue;
		P ans=det(n-1);
		g[i]=ans.y;
	}
	int ans=0;
	for(i=1;i<=mx;i++){
		for(j=i;j<=mx;j+=i)
			f[i]=(1ll*f[i]+1ll*mu[j/i]*g[j])%mod;
		ans=(1ll*ans+1ll*i*f[i])%mod;
	}
	printf("%d\n",ans);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章