• 【BZOJ】1016: [JSOI2008]最小生成树计数(kruskal+特殊的技巧)


    http://www.lydsy.com/JudgeOnline/problem.php?id=1016

    想也想不到QAQ

    首先想不到的是:题目有说,具有相同权值的边不会超过10条。

    其次:老是去想组合计数怎么搞。。。。。。。于是最sb的暴力都不会了。。

    所以这题暴力搞就行了orz

    依次加边,每一种边的方案数乘起来就是方案了。

    注意并查集不能路径压缩,否则在计数的时候会waQAQ因为并查集的路径压缩是不可逆的QAQ

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <set>
    #include <map>
    using namespace std;
    typedef long long ll;
    #define pii pair<int, int>
    #define mkpii make_pair<int, int>
    #define pdi pair<double, int>
    #define mkpdi make_pair<double, int>
    #define pli pair<ll, int>
    #define mkpli make_pair<ll, int>
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << (#x) << " = " << (x) << endl
    #define error(x) (!(x)?puts("error"):0)
    #define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
    #define printarr1(a, b) for1(_, 1, b) cout << a[_] << '	'; cout << endl
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    inline const int max(const int &a, const int &b) { return a>b?a:b; }
    inline const int min(const int &a, const int &b) { return a<b?a:b; }
    
    const int N=105, M=1005, MD=31011;
    int n, m, cnt, p[N], ans=1, sum;
    struct dat { int x, y, w; }e[M], a[M];
    inline const bool cmp(const dat &a, const dat &b) { return a.w<b.w; }
    inline const int ifind(const int &x) { return x==p[x]?x:ifind(p[x]); }
    
    void dfs(int now, int s, const int &x) {
    	if(now>a[x].y) {
    		if(s==a[x].w) ++sum;
    		return;
    	}
    	dfs(now+1, s, x);
    	int fx=ifind(e[now].x), fy=ifind(e[now].y);
    	if(fx!=fy) { p[fx]=fy; dfs(now+1, s+1, x); p[fx]=fx; p[fy]=fy; }
    }
    
    int main() {
    	read(n); read(m);
    	for1(i, 1, m) read(e[i].x), read(e[i].y), read(e[i].w);
    	for1(i, 1, n) p[i]=i;
    	sort(e+1, e+1+m, cmp);
    	int ed=0;
    	for1(i, 1, m) {
    		if(e[i].w!=e[i-1].w) a[++cnt].x=i, a[cnt-1].y=i-1;
    		int fx=ifind(e[i].x), fy=ifind(e[i].y);
    		if(fx!=fy) {
    			p[fx]=fy;
    			++a[cnt].w;
    			++ed;
    		}
    	}
    	if(ed!=n-1) { puts("0"); return 0; }
    	a[cnt].y=m;
    	for1(i, 1, n) p[i]=i;
    	for1(i, 1, cnt) {
    		sum=0;
    		dfs(a[i].x, 0, i);
    		ans=(ans*sum)%MD;
    		for1(j, a[i].x, a[i].y) {
    			int fx=ifind(e[j].x), fy=ifind(e[j].y);
    			if(fx!=fy) p[fx]=fy;
    		}
    	}
    	print(ans);
    
    	return 0;
    }
    

      


    Description

    现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。

    Input

    第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。

    Output

    输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。

    Sample Input

    4 6
    1 2 1
    1 3 1
    1 4 1
    2 3 2
    2 4 1
    3 4 1

    Sample Output

    8

    HINT

     

    Source

     
  • 相关阅读:
    Unity3D笔记十六 输入输出-键盘事件、鼠标事件
    Unity3D笔记十五 碰撞、移动
    Unity3D笔记十四 力
    Unity3D笔记十三 摄像机之间切换
    the pointer this
    argc[] and *argv[]
    Square Detector
    pointer1
    OpenCV1
    OpenCV
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4099713.html
Copyright © 2020-2023  润新知