BZOJ1016: [JSOI2008]最小生成树计数 深搜+并查集
Posted hxer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ1016: [JSOI2008]最小生成树计数 深搜+并查集相关的知识,希望对你有一定的参考价值。
最小生成树计数
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
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
思路:为什么会有多棵最小生成树?由于边权值相等的构成了环,所以可以从中选出若干条权值相等的边(保证连通性即可)而得到了多棵MST;
启发:直接按照边的权值排序,贪心地从小到大取边,在边权相等的所有边中进行深搜,因为在MST中,每种边权的边的个数和作用是确定的,再利用乘法原理即可得到全部边得到的MST的个数了;
编程细节:重新创建一个结构体,用来存储每种边权的边的id的左右边界以及这种边权的个数,在深搜中使用朴素的并查集来判断是否出现环即可;并查集不能进行压缩,否则MST的数量会减少了;
#include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<vector> #include<cmath> #include<stdlib.h> #include<time.h> #include<stack> #include<set> #include<map> #include<queue> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) #define MSi(a) memset(a,0x3f,sizeof(a)) #define inf 0x3f3f3f3f #define lson l, m, rt << 1 #define rson m+1, r, rt << 1|1 typedef pair<int,int> PII; #define A first #define B second #define MK make_pair template<typename T> void read1(T &m) { T x=0,f=1;char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} m = x*f; } template<typename T> void read2(T &a,T &b){read1(a);read1(b);} template<typename T> void read3(T &a,T &b,T &c){read1(a);read1(b);read1(c);} template<typename T> void out(T a) { if(a>9) out(a/10); putchar(a%10+‘0‘); } const int mod = 31011; const int N = 111; const int M = 1011; struct edge{int u,v,w;}e[M]; bool cmp(const edge& a,const edge& b){return a.w < b.w;} struct data{int l,r,s;}d[M]; int f[N],sum; int Find(int a) { return a == f[a] ?f[a]:Find(f[a]);// ** } void dfs(int x,int now,int p) { if(now == d[x].r+1){ if(p == d[x].s) sum++; return ; } int fu = Find(e[now].u), fv = Find(e[now].v); if(fu != fv){ f[fu] = fv; dfs(x,now+1,p+1); f[fu] = fu; } dfs(x,now+1,p); } int main() { int n,m; read2(n,m); rep1(i,1,m) read3(e[i].u,e[i].v,e[i].w); sort(e+1, e+1+m,cmp); int cnt = 0,tot = 0; rep1(i,1,n) f[i] = i; rep1(i,1,m){ if(e[i].w != e[i - 1].w){ d[cnt].r = i - 1; d[++cnt].l = i; } int fu = Find(e[i].u), fv = Find(e[i].v); if(fu != fv){ f[fu] = fv; tot++; d[cnt].s++;// 边权相同的边的个数 } } d[cnt].r = m; if(tot != n - 1)return puts("0"),0;//判断图是否连通 rep1(i,1,n) f[i] = i; int ans = 1; rep1(i,1,cnt){ sum = 0; dfs(i,d[i].l,0); ans = ans*sum%mod; rep1(j,d[i].l,d[i].r){ int u = e[j].u, v = e[j].v; int fu = Find(u), fv = Find(v); if(fu != fv) f[fu] = fv; } } cout<<ans; return 0; }
以上是关于BZOJ1016: [JSOI2008]最小生成树计数 深搜+并查集的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)
BZOJ1016:[JSOI2008]最小生成树计数——题解