[省选联考 2020 A 卷]作业题

一、题目

点此看题

二、解法

学了好久的前置芝士才切掉这道题 q w q qwq qwq

首先要知道欧拉反演(适用范围较小,但是有些情况比莫比乌斯反演简单):
n = ∑ d ∣ n ϕ ( d ) n=\sum_{d|n}\phi(d) n=dnϕ(d)那么原式就可以化为:
∑ e w i × ∑ d ∣ e w i ϕ ( d ) \sum e_{w_i}\times \sum_{d|e_{w_i}}\phi(d) ewi×dewiϕ(d)老套路,先枚举 d d d
∑ d ∣ e w i ϕ ( d ) × ∑ e w i \sum_{d|e_{w_i}}\phi(d)\times \sum e_{w_i} dewiϕ(d)×ewi那么我们只需要枚举 d d d,把边权为 d d d的倍数拿去跑矩阵树,但是我们的矩阵树解决的是边权乘积,这里要求边权和怎么办呢?

那就转化成乘积呗!只不过我们要对边权有一些改动,把边权变为 1 + e w i x 1+e_{w_i}x 1+ewix,那么答案就是 1 1 1次项系数,为保证复杂度,整个过程需要在 m o d    x 2 \mod x^2 modx2的条件下进行,四则运算也需要重新定义,加减乘法都不难,着重讲一下除法。

除法求出除数的逆元即可,假如我们要求 ( a + b x ) (a+bx) (a+bx)的逆元,可以列出方程: ( a + b x ) ( c + d x ) = 1 m o d    x 2 (a+bx)(c+dx)=1\mod x^2 (a+bx)(c+dx)=1modx2,用待定系数法求解得: c = a − 1 , d = − a − 2 b c=a^{-1},d=-a^{-2}b c=a1,d=a2b,然后就可以用乘法的方法了。

这道题是经典的二合一,第一部分用莫比乌斯反演也不难做,主要是考场上不会矩阵树定理 q w q qwq qwq

#include 
#include 
#include 
using namespace std;
const int M = 505;
const int N = 200005;
const int MOD = 998244353;
#define pii pair
#define make make_pair
#define f first
#define s second
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,mx,ans,x[M],y[M],w[M],b[N];
int cnt,p[N],phi[N];pii g[M][M];
void fuck(int x)
{
	for(int i=1;i*i<=x;i++)
		if(x%i==0)
		{
			b[i]++;
			if(i*i!=x) b[x/i]++;
		}
}
void init(int n)
{
	phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!phi[i])
		{
			phi[i]=i-1;
			p[++cnt]=i;
		}
		for(int j=1;j<=cnt && i*p[j]<=n;j++)
		{
			if(i%p[j]==0)
			{
				phi[i*p[j]]=phi[i]*p[j];
				break;
			}
			phi[i*p[j]]=phi[i]*phi[p[j]];
		}
	}
}
int qkpow(int a,int b)
{
	int r=1;
	while(b>0)
	{
		if(b&1) r=1ll*r*a%MOD;
		a=1ll*a*a%MOD;
		b>>=1;
	}
	return r;
}
pii operator + (pii a,pii b)
{
	return make((a.f+b.f)%MOD,(a.s+b.s)%MOD);
}
pii operator - (pii a,pii b)
{
	return make((a.f-b.f)%MOD,(a.s-b.s)%MOD);
}
pii operator * (pii a,pii b)
{
	return make(1ll*a.f*b.f%MOD,(1ll*a.f*b.s+1ll*a.s*b.f)%MOD);
}
pii operator / (pii a,pii b)
{
	int inv=qkpow(b.f,MOD-2);
	return make(1ll*a.f*inv%MOD,(1ll*a.s*b.f-1ll*a.f*b.s)%MOD*inv%MOD*inv%MOD);
}
pii guass()
{
	pii ans=make(1,0);int rev=0;
	for(int i=1;i<n;i++)
	{
		for(int j=i+1;j<n;j++)
			if(!g[i][i].f && g[j][i].f)
			{
				rev^=1;
				swap(g[i],g[j]);
				break;
			}
		for(int j=i+1;j<n;j++)
		{
			if(!g[j][i].f) continue;
			pii f=g[j][i]/g[i][i];
			for(int k=i;k<n;k++)
				g[j][k]=g[j][k]-g[i][k]*f;
		}
	}
	for(int i=1;i<n;i++) ans=ans*g[i][i];
	return rev?make_pair(0,0)-ans:ans;
}
int solve(int t)
{
	memset(g,0,sizeof g);
	for(int i=1;i<=m;i++)
	{
		if(w[i]%t) continue;
		g[x[i]][x[i]]=g[x[i]][x[i]]+make(1,w[i]);
		g[y[i]][y[i]]=g[y[i]][y[i]]+make(1,w[i]);
		g[x[i]][y[i]]=g[x[i]][y[i]]-make(1,w[i]);
		g[y[i]][x[i]]=g[y[i]][x[i]]-make(1,w[i]);
	}
	return guass().s;
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		x[i]=read();y[i]=read();w[i]=read();
		fuck(w[i]);
		mx=max(mx,w[i]);
	}
	init(mx);
	for(int i=1;i<=mx;i++)
	{
		if(b[i]<n-1) continue;
		ans=(ans+1ll*phi[i]*solve(i))%MOD;
	}
	printf("%d\n",(ans+MOD)%MOD);
}

你可能感兴趣的:(矩阵树定理,莫比乌斯反演)