ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

最大生成树计数

2022-01-17 23:33:32  阅读:144  来源: 互联网

标签:return 最大 int 生成 计数 MAXN using MOD


最大生成树计数

最近做到了一道题目,有关于最大生成树计数的,这里来说一下,求解最大生成树数量的方法。

题意

给你一个 \(n\) 个点 \(m\) 条边的无向图,每个边有权值,求出来这个图里,最大生成树的个数。

答案对于 \(998244353\) 取模。

题解

我们考虑一个事情,就是说,对于给定的图的任意的一个最大生成树,对于一个固定的权值 \(w\),他们对应的权值为 \(w\) 的边的个数相同。

所以,对于每个权值,分别做,计数就好了。

我们考虑已经做了一些大的权值,然后现在要做 \(w\) 权值的边的计数。

我们首先,对于之前的那些大于 \(w\) 的边,会形成一些连通块,我们对于 \(w\) 的边,只做连接两个不同连通块的边所构成的生成树的计数(这里把之前的一个连通块看成了一个点),然后用矩阵树定理得到答案即可。

需要注意的是,我们可能做到 \(w\) 的时候图是不连通的,所以,需要对于每个大于等于 \(w\) 的边的连通块分别运用矩阵树定理求解即可。

这里给出一道例题。

【2022省选十连测 Day 3】treecnt - 题目 - Zhengrui Online Judge (zhengruioi.com)

在这道题目中,我们只要对于两个点 \(x,y\) 之间,连一条 \(w(i,j)\) 的边,其中 \(w(i,j)\) 为有多少个限制里同时包含 \(i\) 点和 \(j\) 点。然后,我们一个符合要求的生成树就是权值为 \(\sum S_i - K\) 的生成树,然后同时这个生成树权值肯定最大,所以,我们对于最大生成树计数即可。

#include <bits/stdc++.h>
const int MAXN = 505, MOD = 998244353;
using std::cin;
using std::cout;
using std::bitset;
using std::sort;
using std::vector;
using std::swap;
struct Edge {
	int x, y, w, key;
} e[MAXN * MAXN];
int N, K, ecnt, f1[MAXN], f2[MAXN], tot, id[MAXN], A[MAXN][MAXN];
char s[MAXN];
bitset<2010> bs[MAXN];
vector<Edge> v[MAXN * MAXN];
int find1(int x) {
	return f1[x] == x ? x : f1[x] = find1(f1[x]);
}
int find2(int x) {
	return f2[x] == x ? x : f2[x] = find2(f2[x]);
}
auto Mod = [] (int x) {
	if (x >= MOD) {
		return x - MOD;
	}
	else if (x < 0) {
		return x + MOD;
	}
	else {
		return x;
	}
};
auto Ksm = [] (int x, int y) -> int {
	int ret = 1;
	for (; y; y >>= 1, x = (long long) x * x % MOD) {
		if (y & 1) {
			ret = (long long) ret * x % MOD;
		}
	}
	return ret;
};
int det(int m) {
	int ret = 1;
	for (int i = 1; i <= m; ++i) {
		for (int j = i; j <= m; ++j) {
			if (A[j][i]) {
				for (int k = i; k <= m; ++k) {
					swap(A[i][k], A[j][k]);
				}
				if (j != i) {
					ret = Mod(-ret);
				}
				break;
			}
		}
		ret = (long long) ret * A[i][i] % MOD;
		int invl = Ksm(A[i][i], MOD - 2);
		for (int j = i + 1; j <= m; ++j) {
			if (A[j][i]) {
				int mul = (long long) invl * A[j][i] % MOD;
				for (int k = i; k <= m; ++k) {
					A[j][k] = Mod(A[j][k] - (long long) mul * A[i][k] % MOD);
				}
			}
		}
	}
	return ret;
}
int calc(int x) {
	int cnt = 0;
	for (auto &i: v[x]) {
		if (!id[find2(i.x)]) {
			id[f2[i.x]] = ++cnt;
		}
		if (!id[find2(i.y)]) {
			id[f2[i.y]] = ++cnt;
		}
		int x = id[f2[i.x]], y = id[f2[i.y]];
		A[x][x] = Mod(A[x][x] + i.w);
		A[y][y] = Mod(A[y][y] + i.w);
		A[x][y] = Mod(A[x][y] - i.w + MOD);
		A[y][x] = Mod(A[y][x] - i.w + MOD);
	}
	for (auto &i: v[x]) {
		id[f2[i.x]] = 0;
		id[f2[i.y]] = 0;
	}
	for (auto &i: v[x]) {
		if (find2(i.x) != find2(i.y)) {
			f2[f2[i.x]] = f2[i.y];
		}
	}
	int ret = det(cnt - 1);
	for (int i = 1; i <= cnt; ++i) {
		for (int j = 1; j <= cnt; ++j) {
			A[i][j] = 0;
		}
	}
	return ret;
}
int main() {
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> N >> K;
	for (int i = 1, x; i <= N - 1; ++i) {
		for (int j = i + 1; j <= N; ++j) {
			cin >> x;
			e[++ecnt] = {i, j, x, 0};
		}
	}
	int S = 0;
	for (int i = 1; i <= K; ++i) {
		cin >> s + 1;
		for (int j = 1; j <= N; ++j) {
			bs[j].set(i, s[j] == '1');
			S += s[j] == '1';
		}
	}
	for (int i = 1; i <= ecnt; ++i) {
		e[i].key = (bs[e[i].x] & bs[e[i].y]).count();
	}
	for (int i = 1; i <= N; ++i) {
		f1[i] = i;
	}
	sort(e + 1, e + 1 + ecnt, [&] (const Edge &a, const Edge &b) -> int {
		return a.key > b.key;
	});
	int singercoder = 0;
	for (int i = 1; i <= ecnt; ++i) {
		if (find1(e[i].x) != find1(e[i].y)) {
			f1[f1[e[i].x]] = f1[e[i].y];
			singercoder += e[i].key;
		}
	}
	if (singercoder != S - K) {
		cout << 0 << '\n';
		return 0;
	}
	for (int i = 1; i <= N; ++i) {
		f1[i] = f2[i] = i;
	}
	int ANS = 1;
	for (int i = 1, p; i <= ecnt; i = p + 1) {
		p = i;
		while (p < ecnt && e[p + 1].key == e[i].key) {
			++p;
		}
		for (int j = i; j <= p; ++j) {
			if (find1(e[j].x) != find1(e[j].y)) {
				f1[f1[e[j].x]] = f1[e[j].y];
			}
		}
		for (int j = i; j <= p; ++j) {
			if (find2(e[j].x) == find2(e[j].y)) {
				continue;
			}
			if (!id[find1(e[j].x)]) {
				id[find1(e[j].x)] = ++tot;
			}
			v[id[f1[e[j].x]]].push_back(e[j]);
		}
		for (int j = i; j <= p; ++j) {
			id[f1[e[j].x]] = 0;
		}
		for (int j = 1; j <= tot; ++j) {
			ANS = (long long) ANS * calc(j) % MOD;
			v[j].clear();
		}
	}
	cout << ANS << '\n';
	return 0;
}

标签:return,最大,int,生成,计数,MAXN,using,MOD
来源: https://www.cnblogs.com/siriehn-nx/p/15815894.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有