最小生成树计数模版

typedef long long ll;
const int N = 105;    //点的个数
const int M = 1005;  //边的个数
//点标从1-n MOD是long long
struct node {
	int set[N];
	void init(int n) {
		for (int i = 0; i <= n; i++) set[i] = i;
	}
	int find(int x) {
		return x == set[x] ? x : set[x] = find(set[x]);
	}
	int Union(int x, int y) {
		int xx = find(x);
		int yy = find(y);
		if (xx == yy) return -1;
		set[xx] = yy;
		return 1;
	}
}a, b, c;

struct Node {
	int u, v, dis;
}edge[M];
int edgenum;
void add(int u, int v, int d){
	Node E = { u, v, d };
	edge[++edgenum] = E;
}

bool visit[N];
vector<int> g[N];
ll p[N][N], deg[N][N];
int cmp(Node a, Node b) {
	return a.dis < b.dis;
}
ll DET(ll a[][N], int n, ll MOD)
{
	int i, j, k;
	ll temp = 1, t;
	for (i = 0; i < n; i++) for (j = 0; j < n; j++) a[i][j] %= MOD;
	for (i = 1; i < n; i++)
	{
		for (j = i + 1; j < n; j++) while (a[j][i])
		{
			t = a[i][i] / a[j][i];
			for (k = i; k < n; k++)
			{
				a[i][k] -= a[j][k] * t;
				a[i][k] %= MOD;
			}
			for (k = i; k < n; k++)
				swap(a[i][k], a[j][k]);

			temp = -temp;
		}
		temp = temp*a[i][i] % MOD;
	}
	return (temp + MOD) % MOD;
}

ll cal_MST_count(int n, ll MOD) {
	sort(edge + 1, edge + edgenum + 1, cmp);
	int pre = edge[1].dis;
	ll ans = 1;
	a.init(n);
	b.init(n);
	memset(visit, 0, sizeof(visit));
	memset(deg, 0, sizeof(deg));
	for (int i = 0; i <= n; i++) g[i].clear();
	for (int t = 1; t <= edgenum + 1; t++)
	{
		if (edge[t].dis != pre || t == edgenum + 1)
		{
			for (int i = 1, k; i <= n; i++) if (visit[i])
			{
				k = b.find(i);
				g[k].push_back(i);
				visit[i] = 0;
			}
			for (int i = 1; i <= n; i++)
			if (g[i].size())
			{
				memset(p, 0, sizeof(p));
				for (int j = 0; j < g[i].size(); j++)
				for (int k = j + 1, x, y; k < g[i].size(); k++)
				{
					x = g[i][j];
					y = g[i][k];
					p[j][k] = p[k][j] = -deg[x][y];
					p[j][j] += deg[x][y];
					p[k][k] += deg[x][y];
				}
				ans = ans*DET(p, g[i].size(), MOD) % MOD;
				for (int j = 0; j < g[i].size(); j++) a.set[g[i][j]] = i;
			}
			memset(deg, 0, sizeof(deg));
			for (int i = 1; i <= n; i++)
			{
				b.set[i] = a.find(i);
				g[i].clear();
			}
			if (t == edgenum + 1) break;
			pre = edge[t].dis;
		}
		int x = a.find(edge[t].u);
		int y = a.find(edge[t].v);
		if (x == y) continue;
		visit[x] = visit[y] = 1;
		b.Union(x, y);
		deg[x][y]++;
		deg[y][x]++;
	}
	if (!edgenum) return 0;
	for (int i = 2; i <= n; i++)
	if (b.find(i) != b.find(1))
		return 0;
	return ans;
}
void init(){ edgenum = 0; }
时间: 2024-09-17 20:36:57

最小生成树计数模版的相关文章

JSOI 2008 最小生成树计数

JSOI 2008 最小生成树计数 今天的题目终于良心一点辣 一个套路+模版题. 考虑昨天讲的那几个结论,我们有当我们只保留最小生成树中权值不超过 $ k $ 的边的时候形成的联通块是一定的. 我们可以先拿 kruskal 跑一棵最小生成树,然后我们可以从小到大枚举边权,把所有除开枚举到的边权的边全部加入并且缩点.现在我们就在这个缩点后的点集进行生成树计数就好了.答案就是每种边权算出答案的积. 因为我们知道,连入 $ k $ 边权的边后对于 $ 1 $ 到 $ k - 1 $ 的边加入后的最小生

BZOJ_1016_[JSOI2008]_最小生成树计数_(dfs+乘法原理)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1016 给出一张图,其中具有相同权值的边的数目不超过10,求最小生成树的个数. 分析 生成树的计数有一个什么什么算法... 我真的企图研究了...但是智商捉急的我实在看不懂论文... 所以最后还是写了暴力... 当然暴力也要靠正确的姿势的. 首先来看一个结论: 同一张图的所有最小生成树中,边权值相同的边的数目是一定的. 也就是说,假如某一张图的某一棵最小生成树由边权值为1,1,2,2,2,3的

【BZOJ】【1016】【JSOI2008】最小生成树计数

Kruskal/并查集+枚举 唉我还是too naive,orz Hzwer 一开始我是想:最小生成树删掉一条边,再加上一条边仍是最小生成树,那么这两条边权值必须相等,但我也可以去掉两条权值为1和3的,再加上权值为2和2的,不也满足题意吗?事实上,如果这样的话……最小生成树应该是1和2,而不是1和3或2和2!!! 所以呢?所以对于一个图来说,最小生成树有几条边权为多少的边,都是固定的!所以我们可以做一遍Kruskal找出这些边权,以及每种边权出现的次数.然后,对于每种边权,比方说出现了$v_i$

JSOI2008 最小生成树计数

题解: 最小生成树的两个性质: 1.边权相等的边的个数一定. 2.做完边权为w的所有边时,图的连通性相同. 证明: 1.边权相等的边的个数不一样的话就不会都同时是最小生成树了. 2.假设每种方法的做完边权为w的连通性不同,那么假设i边和j边没有同时被选,那么我们完全可以在一种方案中加入i边(或j边),使得连通性增强,而后面费用更大的边用的更少,这样与这是最小生成树矛盾.于是,命题得证. 代码:不知为何,下面程序有bug,什么时候再回来A掉…… 1 type node1=record 2 x,y,

【bzoj1016】 JSOI2008—最小生成树计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1016 (题目链接) 题意:求图的最小生成树计数. Solution  %了下题解,发现要写矩阵树,150++的程序什么鬼.于是就蒯了hzwer的简便方法.  将边按照权值大小排序,将权值相同的边分到一组,统计下每组分别用了多少条边.然后对于每一组进行dfs,判断是否能够用这一组中的其他边达到相同的效果.最后把每一组的方案数相乘就是答案.  注意并查集不要压缩路径,不然的话不好回溯. 代码: //

BZOJ 题目1016: [JSOI2008]最小生成树计数(Kruskal+Matrix_Tree)

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 3569  Solved: 1425 [Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对3101

1016: [JSOI2008]最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 6200  Solved: 2518[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的 最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生 成树可能很多,所以你只需要输出方案数对3101

[JSOI2008][BZOJ1016] 最小生成树计数

1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3379  Solved: 1336[Submit][Status][Discuss] Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的

【BZOJ】1016: [JSOI2008]最小生成树计数 深搜+并查集

最小生成树计数 Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的最小生成树 可能很多,所以你只需要输出方案数对31011的模就可以了. Input 第 一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数.每个节点用1~n的整数编号.接下来的m行,每行包含两个整数:a,