题意
给定无向带权简单图,其生成树的价值定义为树内边权之和乘以其 gcd \gcd gcd,求其所有生成树的价值和。 n ≤ 30 n\leq 30 n≤30, w (边权) ≤ 152501 w{\tiny\text{(边权)}} \leq 152501 w(边权)≤152501。
题解
考虑当 gcd \gcd gcd 确定时所有生成树的边权和之和,然后把它转化为 gcd \gcd gcd 为 d d d 的倍数时所有生成树的边权和之和再容斥一下。
先枚举 d d d,把所有 d d d 的倍数的边提出来并判断其连通性,如果不连通则 d d d 的倍数都不连通。不连通的则没有计算的必要。
接着考虑如何计算生成树的权值和。对于权值为 w w w 的边,我们算基尔霍夫矩阵时将它的边权当做 1 + w x 1+wx 1+wx,矩阵树定理算完行列式后一次项系数即为答案(对于一条权值为 w w w 的边,它对答案的贡献就是其权值乘以固定这条边后的生成树数量,也就是 w x wx wx 乘以一个常数项)。
求 a + b x a+bx a+bx 的逆元可以考虑 1 + c x 1+cx 1+cx 的逆元是 1 − c x 1-cx 1−cx,然后就好算了。
还有个玄学剪枝就是所有选出来的边的 gcd \gcd gcd 不为 d d d 的话显然没有计算的意义。
代码:
/**********
Author: WLBKR5
Problem: loj 3304, luogu 6624
Name: ×÷ÒµÌâ
Source: ÁªºÏÊ¡Ñ¡ 2020 A¾í
Algorithm: ¾ØÕóÊ÷¶¨Àí
Date: 2020/06/24
Statue: accepted
Submission: loj.ac/submission/844872, www.luogu.com.cn/record/34630205
**********/
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans;
}
const int N=32,W=1.6e5,mod=998244353;
int w[N][N],n;
inline int qpow(int x,int y){
int ans=1;
while(y){
if(y&1)ans=ans*1ll*x%mod;
x=x*1ll*x%mod;
y>>=1;
}
return ans;
}
struct F{
int a,b;//a+bx
F():a(0),b(0){}
F(int x,int y=0):a(x),b(y){if(a>=mod)a-=mod;if(b>=mod)b-=mod;}
operator bool(){ return a||b; }
};
inline F operator* (const F &a,const F &b){
return F(a.a*1ll*b.a%mod,(a.a*1ll*b.b+a.b*1ll*b.a)%mod);
}
inline F operator+ (const F &a,const F &b){ return F((a.a+b.a),(a.b+b.b)); }
inline F operator- (const F &a,const F &b){ return F((a.a+mod-b.a),(a.b+mod-b.b)); }
inline F inv(const F &a){
int i=qpow(a.a,mod-2);
F b(a*i);
return F(b.a,mod-b.b)*F(i);
}
ostream& operator<< (ostream &out,const F &a){
out<<a.a<<"+"<<a.b<<"x";
return out;
}
F a[N][N];
int gauss(){
F ans(1);
for(register int i=1;i<n;i++){
if(!a[i][i])return 0;
ans=ans*a[i][i];
F v=inv(a[i][i]);
for(register int j=i+1;j<n;j++){
if(!a[j][i])continue;
F t=a[j][i]*v;
for(register int k=i;k<=n;k++)a[j][k]=a[j][k]-a[i][k]*t;
}
}
return ans.b;
}
int res[W];
bool ok[W];
int fa[N];
inline int _(int x){ return fa[x]==x?x:fa[x]=_(fa[x]); }
inline int gcd(int x,int y){
return x?gcd(y%x,x):y;
}
int main(){
n=getint();
int m=getint(),maxw=0;
for(int i=0;i<m;i++){
int x=getint(),y=getint(),z=getint();
w[x][y]=w[y][x]=z;maxw=max(maxw,z);
}
int ans=0;
memset(ok,1,sizeof(bool)*maxw+10);
for(int d=1;d<=maxw;d++){
if(!ok[d])continue;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(w[i][j]&&w[i][j]%d==0)
fa[_(i)]=_(j);
for(int i=2;i<=n;i++){
if(_(i)!=_(1)){
ok[d]=0;
break;
}
}
gg:;
if(!ok[d])for(int i=d+d;i<=maxw;i+=d)ok[i]=0;
}
for(int d=maxw;d>=1;d--){
if(!ok[d])continue;
int g=0;
for(int i=1;i<=n;i++)for(int j=i;j<=n;j++){
if(w[i][j]&&w[i][j]%d==0)g=gcd(g,w[i][j]);
a[j][i]=a[i][j]=F();
}
if(g>d)continue;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(w[i][j]&&w[i][j]%d==0){
a[j][i]=a[i][j]=F(mod-1,mod-w[i][j]);
a[i][i]=a[i][i]+F(1,w[i][j]);
a[j][j]=a[j][j]+F(1,w[i][j]);
}
res[d]=gauss();
for(int i=d+d;i<=maxw;i+=d)res[d]=(res[d]+mod-res[i])%mod;
ans=(ans+res[d]*1ll*d)%mod;
}
cout<<ans;
return 0;
}