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

题目链接:点击打开链接

裸题


#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
	if (x <0) {
		putchar('-');
		x = -x;
	}
	if (x>9) pt(x / 10);
	putchar(x % 10 + '0');
}
using namespace std;
typedef long long ll;
const int N = 105;    //点的个数
const int M = 1005;  //边的个数
//点标从1-n
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; }
int n, m, u, v, d;
ll mod;
int main(){
	while (cin >> n >> m){
		init();
		while (m--){
			rd(u); rd(v); rd(d);
			add(u, v, d);
		}
		pt(cal_MST_count(n, 31011LL)); putchar('\n');
	}
	return 0;
}


你可能感兴趣的:(BZOJ1016 && JSOI2008] 最小生成树计数)