題目
正解
大套路題。
看到之後是個人都知道要先反演一下推推式子:
(爲了方便表示,題目中的直接用表示了)
我們要求的是後面的那個東西。用人話說就是:所有邊權爲的倍數的邊,組成的生成樹的邊權和之和。
然後很容易想到矩陣樹。但是由於每個生成樹的貢獻是而不是,所以要用到一個大套路:
邊權爲的邊,丟到基爾霍夫矩陣中的邊權爲。矩陣樹定理搞完之後求一次項係數即是答案。
可以這麼理解:對於某條邊,如果計算它的貢獻,就是要計算它的邊權以及生成樹包含這條邊的方案數。
計算的時候保留常數項和一次項即可。至於求逆,可以手算出的逆元爲。
大優化:計算之前先判斷一下符合條件的邊是否能組成至少一個生成樹。
一個邊權的因數個數最多爲。於是時間大概就是
顯然實際上也不可能真正達到這個時間複雜度。所以是可以過的。
代碼
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 35
#define M N*N
#define MX 152505
#define mo 998244353
#define ll long long
ll qpow(ll x,ll y=mo-2){
ll r=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1)
r=r*x%mo;
return r;
}
int n,m;
struct edge{
int u,v,w;
} ed[M];
int mx,p[MX],np;
bool inp[MX];
int phi[MX];
void getphi(int mx){
inp[1]=1,phi[1]=1;
for (int i=2;i<=mx;++i){
if (!inp[i]){
p[++np]=i;
phi[i]=i-1;
}
for (int j=1;j<=np && i*p[j]<=mx;++j){
inp[i*p[j]]=1;
if (i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
}
struct Po{
int x0,x1;
bool eql0(){return x0==0 && x1==0;}
Po inv(){
int m=qpow(x0);
return (Po){m%mo,(ll)(mo-x1)*m%mo*m%mo};
}
};
Po operator-(Po a){return (Po){(mo-a.x0)%mo,(mo-a.x1)%mo};}
Po operator+(Po a,Po b){return (Po){(a.x0+b.x0)%mo,(a.x1+b.x1)%mo};}
Po operator-(Po a,Po b){return (Po){(a.x0-b.x0+mo)%mo,(a.x1-b.x1+mo)%mo};}
Po operator*(Po a,Po b){return (Po){(ll)a.x0*b.x0%mo,((ll)a.x0*b.x1+(ll)a.x1*b.x0)%mo};}
Po &operator+=(Po &a,Po b){return a=a+b;}
Po &operator-=(Po &a,Po b){return a=a-b;}
Po &operator*=(Po &a,Po b){return a=a*b;}
int determinant(Po m[N][N]){
int o=1;
for (int i=1;i<n;++i){
int j=i;
for (;j<n && m[j][i].eql0();++j);
if (i!=j){
o=mo-o;
for (int k=i;k<n;++k)
swap(m[i][k],m[j][k]);
}
Po inv=-m[i][i].inv();
for (++j;j<n;++j)
if (!m[j][i].eql0()){
Po tmp=inv*m[j][i];
for (int k=i;k<n;++k)
m[j][k]+=m[i][k]*tmp;
}
}
Po r={o,0};
for (int i=1;i<n;++i)
r*=m[i][i];
return r.x1;
}
int dsu[N];
int getdsu(int x){return dsu[x]==x?x:dsu[x]=getdsu(dsu[x]);}
Po mat[N][N];
int calc(int d){
for (int i=1;i<=n;++i)
dsu[i]=i;
for (int i=1;i<=m;++i)
if (ed[i].w%d==0){
int x=getdsu(ed[i].u),y=getdsu(ed[i].v);
if (x!=y)
dsu[x]=y;
}
getdsu(1);
for (int i=2;i<=n;++i)
if (getdsu(i)!=dsu[1])
return 0;
memset(mat,0,sizeof mat);
for (int i=1;i<=m;++i)
if (ed[i].w%d==0){
int u=ed[i].u,v=ed[i].v;
Po w={1,ed[i].w};
mat[u][u]+=w,mat[v][v]+=w;
mat[u][v]-=w,mat[v][u]-=w;
}
return determinant(mat);
}
int main(){
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;++i){
scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].w);
mx=max(mx,ed[i].w);
}
getphi(mx);
ll ans=0;
for (int i=1;i<=mx;++i)
ans+=(ll)phi[i]*calc(i)%mo;
ans%=mo;
printf("%lld\n",ans);
return 0;
}