[省选联考 2020 A 卷] 作业题

P6624 [省选联考 2020 A 卷] 作业题

一道有启发性的矩阵树定理的题。
题目让我们求:

\[\sum_{T}(\sum_{w_i \subseteq T}w_i)*gcd(w_1,...,w_i,...,w_{n - 1}) \]

欧拉反演一下

\[\sum_{d}\phi(d) * \sum_{T, d|w_i}\sum_{w_i \subseteq T}w_i \]

后面部分可以用矩阵树定理来求,将权值和设成一个多项式\(w_ix + 1\),那么最后权值和就为多项式乘积的一次项系数,那么我们只需在\(\mod x^2\) 的意义下维护多项式。

#include<cstdio>
#include<iostream>
#include<algorithm>
#define LL long long
#define IN inline
using namespace std;
const int N = 35 * 35, P = 998244353, Lit = 152501;
int n, p[Lit], bz[Lit + 5], pt, st[N], ed[N], ao[Lit], co[N], m;
LL phi[Lit + 5], w[N]; bool vis[Lit + 5];

IN int read() {
	int t = 0,res = 0; char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) t |= (ch == '-');
	for (; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ 48);
	return t ? -res : res;
}
void init() {
	phi[1] = 1;
	for (int i = 2; i <= Lit; i++) {
		if (!bz[i]) p[++pt] = i, phi[i] = i - 1;
		for (int j = 1; j <= pt && p[j] * i <= Lit; j++) {
			bz[p[j] * i] = 1;
			if (i % p[j] == 0){phi[p[j] * i] = phi[i] * p[j]; break;}
			phi[i * p[j]] = phi[i] * phi[p[j]];
		}
	}
}
LL fpow(LL x, LL y) {
	LL res = 1;
	for (; x; x >>= 1, y = y * y % P)
		if (x & 1) res = res * y % P;
	return res;
}
struct nd{LL f0, f1;}s[N][N];
nd Div(nd x, nd y){
	LL a = x.f1, b = x.f0, c = y.f1, d = y.f0;
	return nd{b * fpow(P - 2, d) % P, (a * d % P - b * c % P + P) * fpow(P - 2, d * d % P) % P};
}
nd Mul(nd x, nd y) {
	LL a = x.f1, b = x.f0, c = y.f1, d = y.f0;
	return nd{b * d % P, (a * d + b * c) % P};
}
nd Sub(nd x, nd y) {
	return nd{(x.f0 - y.f0 + P) % P, (x.f1 - y.f1 + P) % P};
}
void add(int x, int y, int z1, int z0) {
	(s[x][y].f1 += P + z1) %= P, (s[x][y].f0 += P + z0) %= P;
}
LL DET() {
	int fl = 0; nd res = nd{1, 0};
	for (int i = 1; i < n; i++) {
		if (!s[i][i].f0 && !s[i][i].f1) {
			for (int j = i + 1; j < n; j++)
				if (s[j][i].f0 || s[j][i].f1) {
					swap(s[i], s[j]), fl ^= 1; break;
				}
		}
		if (!s[i][i].f0 && !s[i][i].f1) return 0;
		nd inv = Div(nd{1, 0}, s[i][i]);
		for (int j = i + 1; j < n; j++) {
			nd z = Mul(s[j][i], inv);
			for (int k = i; k < n; k++) s[j][k] = Sub(s[j][k], Mul(z, s[i][k]));
		}
		res = Mul(res, s[i][i]);
	}
	return res.f1;
}
int main() {
	n = read(), m = read(), init();
	for (int i = 1; i <= m; i++) {
		st[i] = read(), ed[i] = read(), w[i] = read();
		for (int j = 1; j * j <= w[i]; j++)
			if (w[i] % j == 0) {
				if (!vis[j]) vis[j] = 1;
				if (!vis[w[i] / j]) vis[w[i] / j] = 1;
			}
	}
	int tot = 0;
	for (int i = 1; i <= Lit; i++)
		if (vis[i]) ao[++tot] = i;
	LL ans = 0;
	for (int i = 1; i <= tot; i++) {
		int cnt = 0;
		for (int j = 1; j <= m; j++)
			if (w[j] % ao[i] == 0) co[++cnt] = j;
		if (cnt < n - 1) continue;
		for (int j = 1; j <= cnt; j++) {
			add(st[co[j]], ed[co[j]], -w[co[j]], -1), add(ed[co[j]], st[co[j]], -w[co[j]], -1);
			add(st[co[j]], st[co[j]], w[co[j]], 1), add(ed[co[j]], ed[co[j]], w[co[j]], 1);
		}
		(ans += phi[ao[i]] * DET() % P) %= P;
		for (int j = 1; j < n; j++)
			for (int k = 1; k < n; k++) s[j][k] = nd{0, 0};
	}
	printf("%lld\n", ans);
}

posted @ 2023-02-09 17:29  RiverSheep  阅读(56)  评论(0)    收藏  举报