给定一张 (n) 个点 (m) 条边的带边权的无向图,定义一棵生成树 (T) 的价值为:
[(sum_{i=1}^{n-1}w_{e_i}) imes gcd(w_{e_1}, w_{e_2},cdots,w_{e_{n-1}})
]
其中 (w_{e_i}) 是 (T) 中的边。要求求出所有生成树的价值和。(1 leq n leq 30 , 1 leq m leq frac{n(n-1)}{2} , 1 leq w_i leq 152501) ,无重边自环。
怎么想到的啊。。。
首先如果 (w_i) 全相同,那就直接 ( ext{matrix}) 定理求一下生成树个数即可。但现在 (w_i) 不相同。
化一下式子:
[egin{aligned}
(sum_{i=1}^{n-1}w_{e_i}) imes gcd(w_{e_1}, w_{e_2},cdots,w_{e_{n-1}})
\=(sum_{i=1}^{n-1}w_i{e_i}) imes sum_{d|w_1, d|w_2, cdots ,d|w_{n-1}}varphi(d)
\=sum_{d=1}varphi(d) sum_{d|w_1,d|w_2,cdots,w_{n-1}}sum_{i=1}^{n-1}w_i
end{aligned}
]
现在就只需要对于每个 (d) ,求出所有边权是 (d) 的倍数的边构成的子图的答案。考虑对每条边计算它的贡献,也就是对于这条边来说,有多少棵包含它的生成树。本来如果 (w_i) 相同的话,构造 ( ext{kirchhoff}) 矩阵时会在对应的位置加减1,现在就改成在对应位置加上 (1+w_ix)这样一个多项式,然后给矩阵求个行列式,行列式的一次项系数就是包含这条边的生成树个数乘 (w_i) 。所以我们一开给每条边都这样做,然后计算行列式时对 (x^2) 取模即可。
时间复杂度 (mathcal{O} (n^3sum_{i=1}^{maxw}sigma_0(i)))
#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
#define gc getchar()
#define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
#define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
#define go(u) for(rg int i=head[u];~i;i=e[i].nxt)
using namespace std;
typedef cn int cint;
typedef long long LL;
il void rd(int &x){
x = 0;
rg int f(1); rg char c(gc);
while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
x*=f;
}
cint maxn = 31, maxm = 440, maxw = 152510, mod = 998244353;
int n, m, mx, ans;
int pri[maxw], cnt, isnp[maxw], phi[maxw];
int tot, id[maxm];
il int fpow(int a, int b, int ans = 1){
for(; b; b >>= 1, a = 1ll*a*a%mod)if(b&1)ans = 1ll*ans*a%mod;
return ans;
}
struct node{int fi, se;}kir[maxn][maxn];
il node operator + (cn node &x, cn node &y){return (node){(x.fi+y.fi)%mod, (x.se+y.se)%mod};}
il node operator - (cn node &x, cn node &y){return (node){(x.fi-y.fi+mod)%mod, (x.se-y.se+mod)%mod};}
il node operator * (cn node &x, cn node &y){return (node){1ll*x.fi*y.fi%mod, (1ll*x.fi*y.se+1ll*x.se*y.fi)%mod};}
il node operator / (cn node &x, cn node &y){
rg int inv = fpow(y.fi, mod-2), sinv = 1ll*inv*inv%mod;
return (node){1ll*x.fi*inv%mod, (1ll*x.se*y.fi%mod-1ll*x.fi*y.se%mod+mod)*sinv%mod};
}
struct edge{int u, v, w;}e[maxm];
il void SWAP(node *a, node *b){fp(i, 1, n)swap(a[i], b[i]);}
il int matrix_tree(){
if(tot < n-1)return 0;
node ans = (node){1, 0};
memset(kir, 0, sizeof kir);
fp(i, 1, tot){
++kir[e[id[i]].u][e[id[i]].u].fi, ++kir[e[id[i]].v][e[id[i]].v].fi;
--kir[e[id[i]].u][e[id[i]].v].fi, --kir[e[id[i]].v][e[id[i]].u].fi;
kir[e[id[i]].u][e[id[i]].u].se += e[id[i]].w, kir[e[id[i]].v][e[id[i]].v].se += e[id[i]].w;
kir[e[id[i]].u][e[id[i]].v].se -= e[id[i]].w, kir[e[id[i]].v][e[id[i]].u].se -= e[id[i]].w;
}
fp(i, 1, n)fp(j, 1, n)kir[i][j] = (node){(kir[i][j].fi+mod)%mod, (kir[i][j].se+mod)%mod};
fp(i, 2, n){
if(!kir[i][i].fi){
fp(j, i+1, n)if(kir[j][i].fi){
ans = (node){mod-ans.fi, mod-ans.se}, SWAP(kir[i], kir[j]);
break;
}
}
rg node mlt;
fp(j, i+1, n){
mlt = kir[j][i]/kir[i][i];
fp(k, i, n)kir[j][k] = kir[j][k]-kir[i][k]*mlt;
}
ans = ans*kir[i][i];
}
return ans.se;
}
int main(){
rd(n), rd(m);
fp(i, 1, m)rd(e[i].u), rd(e[i].v), rd(e[i].w), mx = max(mx, e[i].w);
phi[1] = 1;
fp(i, 2, mx){
if(!isnp[i])pri[++cnt] = i, phi[i] = i-1;
for(rg int j = 1; j <= cnt && pri[j]*i <= mx; ++j){
isnp[i*pri[j]] = 1;
if(i%pri[j] == 0){phi[i*pri[j]] = phi[i]*pri[j]; break;}
phi[i*pri[j]] = phi[i]*phi[pri[j]];
}
}
fp(i, 1, mx){
tot = 0;
fp(j, 1, m)if(e[j].w % i == 0)id[++tot] = j;
ans = (ans+1ll*phi[i]*matrix_tree())%mod;
}
printf("%d
", ans);
return 0;
}