zoukankan      html  css  js  c++  java
  • BZOJ-1016: [JSOI2008]最小生成树计数 (kruscal+搜索)

    1016: [JSOI2008]最小生成树计数

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 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

    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

    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 }

     

    未来是什么样,未来会发生什么,谁也不知道。 但是我知道, 起码从今天开始努力, 肯定比从明天开始努力, 要快一天实现梦想。 千里之行,始于足下! ——《那年那兔那些事儿》
  • 相关阅读:
    线段树(已修改+补题
    畅通工程
    线段树
    charles
    flash
    mysql node pool
    node简单操作mysql的类
    小于任意数字的随机数
    文件系统的移植
    驱动
  • 原文地址:https://www.cnblogs.com/keximeiruguo/p/7712152.html
Copyright © 2011-2022 走看看