• 【BZOJ1016】【Luogu P4208】 [JSOI2008]最小生成树计数 最小生成树,矩阵树定理


    蛮不错的一道题,遗憾就遗憾在数据范围会导致暴力轻松跑过。

    最小生成树的两个性质:

    • 不同的最小生成树,相同权值使用的边数一定相同。

    • 不同的最小生成树,将其都去掉同一个权值的所有边,其连通性一致。

    这样我们随便跑一个(MST),就可以知道所有(MST)边的构造情况。由于性质二,我们可以考虑枚举每一种权值的所有边,保留所有非此权值的树边,看可以连出来多少种不同的最小生成树。也就是按照权值构造最小生成树,这个过程满足乘法原理。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int N = 100 + 5;
    const int M = 1000 + 5;
    const int Mod = 31011;
    
    struct Len {
    	int u, v, w;
    	
    	bool operator < (Len rhs) const {
    		return w < rhs.w;
    	}
    }l[M];
    
    vector <Len> v;
    
    int n, m, S[N];
    
    int find (int x) {
    	return S[x] == x ? x : S[x] = find (S[x]);
    }
    
    vector <int> val, use, tot;
    
    vector <int> :: iterator it;
    
    void kruskal () {
    	sort (l + 1, l + 1 + m);
    	for (int i = 0; i <= n; ++i) S[i] = i;
    	for (int i = 1; i <= m; ++i) {
    		int fu = find (l[i].u);
    		int fv = find (l[i].v);
    		it = lower_bound (val.begin (), val.end (), l[i].w); 
    		if (it == val.end ()) {
    			val.push_back (l[i].w);
    			use.push_back (0);
    			tot.push_back (1);
    		} else {
    			tot[it - val.begin ()]++;
    		}
    		if (fu != fv) {
    			S[fu] = fv;
    			it = lower_bound (val.begin (), val.end (), l[i].w); 
    			use[it - val.begin ()]++;
    			v.push_back (l[i]);
    		}
    	}
    }
    
    int mat[N][N];
    
    int gauss (int n) {
    	int ret = 1;
    	for (int i = 1; i <= n; ++i) {
    		for (int k = i + 1; k <= n; ++k) {
    			while (mat[k][i]) {
    				int d = mat[i][i] / mat[k][i];
    				for (int j = i; j <= n; ++j) {
    					(((mat[i][j] -= d * mat[k][j]) %= Mod) += Mod) %= Mod;
    				}
    				swap (mat[i], mat[k]); ret = -ret;
    			}
    		}
    		(((ret *= mat[i][i]) %= Mod) += Mod) %= Mod;
    	}
    	return abs (ret);
    }
    
    void add_edge (int u, int v) {
    	mat[u][u]++; 
    	mat[v][v]++;
    	mat[u][v]--;
    	mat[v][u]--;
    }
    
    int sep[N]; 
    
    int solve () {
    	kruskal ();
    	if (v.size () < n - 1) return 0;
    	int ans = 1;
    	for (int i = 0; i < val.size (); ++i) {
    		memset (mat, 0, sizeof (mat));
    		if (use[i] == 0 || tot[i] == use[i]) continue;
    		for (int j = 0; j <= n; ++j) S[j] = j;
    		for (int j = 0; j < v.size (); ++j) {
    			if (v[j].w != val[i]) {
    				S[find (v[j].u)] = find (v[j].v);
    			}
    		}
    		int cnt = 0;
    		for (int i = 1; i <= n; ++i) {
    			sep[++cnt] = find (i);
    		}
    		sort (sep + 1, sep + 1 + cnt);
    		cnt = unique (sep + 1, sep + 1 + cnt) - sep - 1;
    		for (int j = 1; j <= m; ++j) {
    			if (l[j].w == val[i]) {
    				int fu = find (l[j].u);
    				int fv = find (l[j].v);
    				fu = lower_bound (sep + 1, sep + 1 + cnt, fu) - sep;
    				fv = lower_bound (sep + 1, sep + 1 + cnt, fv) - sep;
    				add_edge (fu, fv);
    			}
    		}
    		(ans *= gauss (use[i])) %= Mod;
    	}
    	return ans;
    }
    
    signed main () {
    //	freopen ("data.in", "r", stdin);
    	cin >> n >> m;
    	for (int i = 1; i <= m; ++i) {
    		static int u, v, w;
    		cin >> u >> v >> w;
    		l[i] = (Len) {u, v, w};
    	}
    	cout << solve () << endl;
    }
    
  • 相关阅读:
    JS定时器不可靠的原因及解决方案
    大厂面试:一个四年多经验程序员的BAT面经(字节、阿里、腾讯)
    Hyperledger Fabric 2.x Java区块链应用
    Hyperledger Fabric 2.x 自定义智能合约
    Hyperledger Fabric 2.x 环境搭建
    Hyperledger Fabric 2.x 动态更新智能合约
    Spring Cloud Gateway现高风险漏洞,建议采取措施加强防护
    ApplicationStartedEvent与ContextStartedEvent有区别吗?
    面对科技公司的制裁,俄罗斯放出封印7年的神兽:RuTracker
    MacOS新功能“通用控制”,多台设备操作互联太方便了!
  • 原文地址:https://www.cnblogs.com/maomao9173/p/10946013.html
Copyright © 2020-2023  润新知