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

复盘 \(\color{black}{\text{n}}\color{red}{\text{ealchen}}\) 神仙讲的题。

完蛋我最近好像和生成树杠上了。

Description

给定一个图 \(G\) ,要对它所有生成树 \(T\) 求这样的一个权值和:

\[val(T) = \Big(\sum_{e \in T} w_e\Big) \cdot \gcd(w_{e_1}\ ,w_{e_2}\ ,...\ ,w_{e_{n - 1}} \;\!) \]

答案对 \(998244353\) 取模。

\(n \leq 20,\ m \leq \frac{n (n - 1)}{2},\ w \leq 152501\)

Analysis

先反演掉烦人的 \(\gcd\) ,然后大概可以通过枚举 \(\gcd\) 然后去快速找单边的贡献,看起来很对/hanx

可能是目前最短的 Analysis 了。

Solution

该换枚举 \(\gcd\) ,可以直接用欧拉反演一步到位:

\[x = \sum_{d | x} \varphi(d) \]

(这是那个狄利克雷卷积的形式)

\[\sum_{T} \Big(\sum_{e \in T} w_e\Big) \cdot \gcd(w_{e_1}\ ,w_{e_2}\ ,...\ ,w_{e_{n - 1}}) \]

\[\Rightarrow \sum_{T} \Big(\sum_{e \in T} w_e\Big) \cdot \sum_{d | \gcd(w_{e_1}\ ,w_{e_2}\ ,...\ ,w_{e_{n - 1}})} \varphi(d) \]

\[\Rightarrow \sum_{T} \Big(\sum_{e \in T} w_e\Big) \cdot \sum_{d = 1} ^ {\max w} \varphi(d) \cdot [d | w_{e_1} \wedge d | w_{e_2}\ \wedge...\wedge d | w_{e_{n - 1}}] \]

\[\Rightarrow \sum_{d = 1} ^ {\max w} \varphi(d) \sum_{T \wedge \forall i,d | w_{e_i}} \Big(\sum_{e \in T} w_e\Big) \]

所以现在来看目的已经很明确了,就是对于每种因数 \(d\) ,我们需要所有是 \(d\) 的倍数的边权,让后让他们组成生成树。

所以说我们只会求数量啊,边权和怎么办呢?

于是有这么一个 trick(算是吧),在 CF917D 里应该是出现过的,大概就是我们让基尔霍夫矩阵的数带上元 \(x\) ,也就是形如 \((w_e x + 1)\) ,我们(手玩手玩)就能发现这个 \(\prod\) 的一次项对应的正好是当前边权存在的次数,最终求和也就是总的边权和。

(具体是因为:对于一种树,能够生成,那么一次项就正好仅包含一条边的权值,对于每条边的元都如此,那么求合起来就是一整棵树的权值和)

具体实现也不需要多项式,应为仅保留一次项和常数项,所以一个 pair 解决一切。

剩下就是多项式的加减乘除,加减很淼,乘展开一下式子 \((ax + b)(cx + d)\) 就行了,接下来是除法:

即我们要求:假定 \(ux + v = \frac{1}{cx + d}\) ,即求 \((ax + b)(ux + v)\)

因为只保留前两项,即可以理解为多项式在 \(\text{mod}\ x ^ 2\) 的意义下。

\[(ux + v)(cx + d) \equiv 1 (\text{mod}\ x ^ 2) \]

所以就有了:

\[ \left\{ \begin{array}{ll} dv = 1 \\ (cv + du)x = 0 \end{array} \right. \]

\(x\) 不可能是零(不然有屁用啊),所以稍微解一下:

\[ \left\{ \begin{array}{ll} u = -\large\frac{c}{d ^ 2} \\ v = \large\frac{1}{d} \end{array} \right. \]

然后你带回 \((ax + b)(ux + v)\) 就可以啦。

Code

Code

/*
 
*/
#include 
using namespace std;

#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define Check(a) freopen(a".in", "r", stdin), freopen(a".ans", "w", stdout);

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair pii;
#define fi first
#define se second
#define mp std::make_pair

const int mod = 998244353;
template 
inline int M(A x) {return x;}
template 
inline int M(A x, B ... args) {return 1ll * x * M(args...) % mod;}

const int N = 32, K = 152505;

inline int ksm(int a, int b) {
	int tm = 1;
	for (; b; b >>= 1, a = M(a, a)) if (b & 1) tm = M(tm, a);
	return tm;
}

inline pii operator + (const pii a, const pii b) {
	pii p = mp(a.fi + b.fi, a.se + b.se);
	(p.fi >= mod) && (p.fi -= mod);
	(p.se >= mod) && (p.se -= mod);
	return p;
}

inline pii operator - (const pii a, const pii b) {
	pii p = mp(a.fi - b.fi, a.se - b.se);
	(p.fi < 0) && (p.fi += mod);
	(p.se < 0) && (p.se += mod);
	return p;
}

inline pii operator * (const pii a, const pii b) {
	pii p = mp(M(a.fi, b.fi), M(a.fi, b.se) + M(a.se, b.fi));
	(p.se >= mod) && (p.se -= mod);
	return p;
}

inline pii operator / (const pii a, const pii b) {
	int in = ksm(b.fi, mod - 2);
	pii p = mp(M(a.fi, in), M(a.se, b.fi) - M(a.fi, b.se));
	(p.se < 0) && (p.se += mod); p.se = M(p.se, in, in);
	return p;
}

int n, m;
pii a[N][N];
struct mdzz {int u, v, w;} p[N * N];

inline void getpii(int di) {
	memset(a, 0, sizeof(a));

	for (int i = 1; i <= m; ++i) {
		if (p[i].w % di) continue;
		
		int u = p[i].u, v = p[i].v;
		a[u][u] = a[u][u] + mp(1, p[i].w); a[v][v] = a[v][v] + mp(1, p[i].w);
		a[u][v] = a[u][v] - mp(1, p[i].w); a[v][u] = a[v][u] - mp(1, p[i].w);
	}
}

inline int del(int n) {
	pii w = mp(1, 0);

	for (int i = 1; i <= n; ++i) {
		for (int j = i + 1; j <= n; ++j) {
			for (pii div; a[i][i].fi > 0; ) {
				div = a[j][i] / a[i][i];

				for (int k = i; k <= n; ++k) {
					a[j][k] = a[j][k] - div * a[i][k];
				}
				swap(a[i], a[j]); w = mp(0, 0) - w;
			}
			swap(a[i], a[j]); w = mp(0, 0) - w;
		}
	}

	for (int i = 1; i <= n; ++i) w = w * a[i][i];

	return w.se;
}

int cn[K], phi[K], ans;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	std::cin >> n >> m;
	int mx = 0;
	for (int i = 1; i <= m; ++i) {
		std::cin >> p[i].u >> p[i].v >> p[i].w;
		mx = std::max(mx, p[i].w);

		for (int j = 1; j * j <= p[i].w; ++j) {
			if (p[i].w % j) continue;
			++cn[j];
			(j * j != p[i].w) && (++cn[p[i].w / j]);
		}
	}

	phi[1] = 1;
	for (int i = 2; i <= mx; ++i) {
		if (phi[i]) continue;
		phi[i] = i - 1;

		for (int j = i << 1; j <= mx; j += i) {
			if (!phi[j]) phi[j] = j;
			phi[j] = phi[j] / i * (i - 1);
		}
	}

	for (int i = 1; i <= mx; ++i) {
		if (cn[i] < n - 1) continue;//无法作为gcd做出贡献
		getpii(i);
		ans += M(phi[i], del(n - 1));
		(ans >= mod) && (ans -= mod);
	}

	std::cout << ans << "\n";

	return 0;
}


深深明白自己的弱小。

posted @ 2022-07-13 16:27  Illusory_dimes  阅读(28)  评论(1编辑  收藏  举报