BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)
Posted 可惜没如果=_=
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)相关的知识,希望对你有一定的参考价值。
1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 6429 Solved: 2611
[Submit][Status][Discuss]
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,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
Output
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
Sample Input
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
HINT
Source
zzt聚聚说什么基尔霍夫矩阵求行列式????laj不会哇QAQ zzt说貌似很有用??? laj心里更苦逼了
然鹅放下基尔霍夫矩阵不谈,貌似……搜索laj也不会QAQ……hzwer Orz
最小生成树貌似有一个奇奇怪怪的定理,就是缩每一个最小生成树的边集的长度都是一样的??比如说这一个最小生成树用了4条边权为2的边,那么另一个最小生成树也一定用了4条边权为2的边qwq
基于这一点,跑一遍kruskal算出一个最小生成树里面每种边出现的次数,因为之前边已经按照边权排过序,所以同一种边在这个序列里是连续的,用一个结构体存第i种边的左端点,第i种边的右端点,以及第i种边的价值(即能使多少个连通块连在一起)。然后搜索枚举每种边,通过搜索算出每种边能够产生的不同最小生成树的个数,最后利用乘法原理乘起来即可
搜索过程中不能路径压缩,因为如果路径压缩的话,回溯的时候不好还原,当搜索到这些边的价值之和与这种边的总价值相等时,sum++;
1 #include "bits/stdc++.h" 2 using namespace std; 3 typedef long long LL; 4 const int MAX1=105; 5 const int MAX2=1005; 6 int n,m,fa[MAX1],tot,sum,ans; 7 struct Edge{ 8 int u,v,w; 9 bool operator < (const Edge &tt) const { 10 return w<tt.w; 11 } 12 }edge[MAX2]; 13 struct Node{int low,high,num;Node(){num=0;}}a[MAX2];int cnt;//存放权值相同的边的最左边的一条,最右边的一条以及产生作用的条数 14 int getfather(int x){return fa[x]==x?x:getfather(fa[x]);} 15 inline int read(){ 16 int an=0,x=1;char c=getchar(); 17 while (c<‘0‘ || c>‘9‘) {if (c==‘-‘) x=-1;c=getchar();} 18 while (c>=‘0‘ && c<=‘9‘) {an=an*10+c-‘0‘;c=getchar();} 19 return an*x; 20 } 21 void dfs(int x,int now,int k){//x表示现在正在处理第x种边,now表示正在处理第i条边,k表示当前合并集合的次数 22 if (now==a[x].high+1){ 23 if (k==a[x].num) sum++; 24 return; 25 } 26 int tx=getfather(edge[now].u); 27 int ty=getfather(edge[now].v); 28 if (tx!=ty){ 29 fa[tx]=ty; 30 dfs(x,now+1,k+1); 31 fa[tx]=tx,fa[ty]=ty;//回溯 32 } 33 dfs(x,now+1,k); 34 } 35 int main(){ 36 freopen ("count.in","r",stdin); 37 freopen ("count.out","w",stdout); 38 int i,j; 39 n=read(),m=read(); 40 for (i=1;i<=m;i++) edge[i].u=read(),edge[i].v=read(),edge[i].w=read(); 41 sort(edge+1,edge+m+1); 42 for (i=1;i<=n;i++) fa[i]=i; 43 for (i=1;i<=m;i++){ 44 if (edge[i].w!=edge[i-1].w) a[++cnt].low=i,a[cnt-1].high=i-1; 45 int tx=getfather(edge[i].u); 46 int ty=getfather(edge[i].v); 47 if (tx!=ty){ 48 fa[tx]=ty; 49 a[cnt].num++; 50 tot++; 51 } 52 } 53 a[cnt].high=m; 54 if (tot!=n-1){puts("0");return 0;} 55 for (i=1;i<=n;i++) fa[i]=i; 56 for (ans=i=1;i<=cnt;i++){ 57 sum=0; 58 dfs(i,a[i].low,0); 59 ans=(ans*sum)%31011; 60 for (j=a[i].low;j<=a[i].high;j++){ 61 int tx=getfather(edge[j].u); 62 int ty=getfather(edge[j].v); 63 if (tx!=ty) 64 fa[tx]=ty; 65 } 66 } 67 printf("%d",ans); 68 return 0; 69 }
以上是关于BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)
BZOJ1016:[JSOI2008]最小生成树计数——题解