题解 LOJ #3304. 「联合省选 2020 A」作业题
首先先把那个 \(\gcd\) 给反演掉:
\[=\sum_{d=1}^W\varphi(d)\sum_{T}\sum_{w\in T,d\mid w}w
\]
后面的部分是个经典的矩阵树应用:将边权改为 \(wx+1\),在模 \(x^2\) 意义下做矩阵树,最后返回一次项系数。
然后直接做就行了。注意需要剪枝:如果边数小于 \(n-1\) 就跳过。
#include<cstdio>
#include<algorithm>
#define fi first
#define se second
#define mp(x, y) std::make_pair(x, y)
typedef long long ll;
typedef std::pair<ll, ll> poly;
const ll mod = 998244353;
const int maxn = 35;
const int W = 152501;
int n, m, edg[W + 5]; ll g[maxn][maxn];
int phi[W + 5], cnt, prime[W + 5];
bool nprime[W + 5];
inline ll fsp(ll a, ll b, const ll &mod = mod, ll res = 1) {
for(a %= mod, b %= mod - 1; b; a = a * a % mod, b >>= 1)
if(b & 1) res = res * a % mod; return res;
}
inline poly operator+(const poly &a, const poly &b) {
return mp((a.fi + b.fi) % mod, (a.se + b.se) % mod);
}
inline poly operator-(const poly &a, const poly &b) {
return mp((a.fi - b.fi) % mod, (a.se - b.se) % mod);
}
inline poly operator*(const poly &a, const poly &b) {
return mp((a.fi * b.se + a.se * b.fi) % mod, a.se * b.se % mod);
}
inline poly operator/(const poly &a, const poly &b) {
ll inv = fsp(b.se, mod - 2);
return mp((a.fi * b.se - a.se * b.fi) % mod * inv % mod * inv % mod, a.se * inv % mod);
}
inline void pre(int N) {
phi[1] = 1;
for(int i = 2; i <= N; ++i) {
if(!nprime[i]) prime[++cnt] = i, phi[i] = i - 1;
for(int j = 1; j <= cnt && i * prime[j] <= N; ++j) {
nprime[i * prime[j]] = 1;
if(i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1);
else { phi[i * prime[j]] = phi[i] * prime[j]; break; }
}
}
}
namespace MatrixTree {
poly Mat[maxn][maxn];
inline ll work() {
ll sig = 1;
for(int i = 1; i < n; ++i) {
if(!Mat[i][i].se) {
bool flag = 0;
for(int j = i + 1; j < n; ++j)
if(Mat[j][i].se) {
flag = 1; std::swap(Mat[i], Mat[j]);
sig = -sig; break;
}
if(!flag) return 0;
}
for(int j = i + 1; j < n; ++j) {
poly inv = Mat[j][i] / Mat[i][i];
for(int k = 1; k < n; ++k)
Mat[j][k] = Mat[j][k] - Mat[i][k] * inv;
}
}
poly res = mp(0, 1);
for(int i = 1; i < n; ++i)
res = res * Mat[i][i];
return res.fi * sig;
}
}
int main() {
scanf("%d%d", &n, &m), pre(W);
for(int i = 1, u, v, w; i <= m; ++i) {
scanf("%d%d%d", &u, &v, &w);
g[u][v] = g[v][u] = w, ++edg[w];
}
for(int p = 1; p <= cnt; ++p)
for(int i = W / prime[p]; i; --i)
edg[i] += edg[i * prime[p]];
ll ans = 0;
for(int d = 1; d <= W; ++d) {
if(edg[d] < n - 1) continue;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
MatrixTree::Mat[i][j] = mp(0, 0);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j < i; ++j) {
if(!g[i][j] || g[i][j] % d) continue;
MatrixTree::Mat[i][i] = MatrixTree::Mat[i][i] + mp(g[i][j], 1);
MatrixTree::Mat[j][j] = MatrixTree::Mat[j][j] + mp(g[i][j], 1);
MatrixTree::Mat[i][j] = MatrixTree::Mat[i][j] - mp(g[i][j], 1);
MatrixTree::Mat[j][i] = MatrixTree::Mat[j][i] - mp(g[i][j], 1);
}
}
(ans += phi[d] * MatrixTree::work()) %= mod;
} printf("%lld", (ans + mod) % mod);
}