题解 LOJ #3304. 「联合省选 2020 A」作业题

题目链接

首先先把那个 (gcd) 给反演掉:

[=sum_{d=1}^Wvarphi(d)sum_{T}sum_{win T,dmid 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);
}
原文地址:https://www.cnblogs.com/whx1003/p/14201307.html