• bzoj 1016 [JSOI2008]最小生成树计数


    [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

    Sample Output

    8
     
    题解:
      就是最小生成树的话每种权值用了多少边是一定的。
      可以证明,两棵最小生成树,不同种类(权值一样的边叫做一类)的边用量是一样的。
      所以说,可以先求一次最小生成树,各个种类的边的用量先知道,然后dfs搜出有多少种
      方式可以联通出当前权值的联通性,因为在求最小生成树中,该权值的边的联通性是一定
      包涵完全的。所以乘法原理即可。
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 
     7 #define mod 31011
     8 #define N 107
     9 #define M 1007
    10 using namespace std;
    11 inline int read()
    12 {
    13     int x=0;char ch=getchar();
    14     while(ch<'0'||ch>'9'){ch=getchar();}
    15     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    16     return x;
    17 }
    18 
    19 int n,m,cnt,tot,ans=1,sum;
    20 int fa[N];
    21 struct edge{int x,y,v;}e[M];
    22 struct data{int l,r,v;}a[M];
    23 
    24 bool cmp(edge a,edge b){return a.v<b.v;}
    25 int find(int x){return x==fa[x]?x:find(fa[x]);}
    26 void dfs(int x,int now,int k)
    27 {
    28      if(now==a[x].r+1)
    29      {
    30          if(k==a[x].v)sum++;
    31          return;
    32      }
    33      int p=find(e[now].x),q=find(e[now].y);
    34      if(p!=q)
    35      {
    36          fa[p]=q;
    37          dfs(x,now+1,k+1);
    38          fa[p]=p;fa[q]=q;
    39      }
    40      dfs(x,now+1,k);
    41 }
    42 int main()
    43 {
    44     n=read();m=read();
    45     for(int i=1;i<=n;i++) fa[i]=i;
    46     for(int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].v=read();
    47     sort(e+1,e+m+1,cmp);
    48     
    49     for(int i=1;i<=m;i++)
    50     {
    51         if(e[i].v!=e[i-1].v){a[++cnt].l=i;a[cnt-1].r=i-1;}
    52         int p=find(e[i].x),q=find(e[i].y);
    53         if(p!=q){fa[p]=q;a[cnt].v++;tot++;}
    54     }
    55     a[cnt].r=m;
    56     if(tot!=n-1){printf("0");return 0;}
    57     
    58     for(int i=1;i<=n;i++) fa[i]=i;
    59     for(int i=1;i<=cnt;i++)
    60     {
    61         sum=0,dfs(i,a[i].l,0);
    62         ans=(ans*sum)%mod;
    63         for(int j=a[i].l;j<=a[i].r;j++)
    64         {
    65             int p=find(e[j].x),q=find(e[j].y);
    66             if(p!=q)fa[p]=q;
    67         }
    68     }//爆搜连边,然后乘法原理即可,因为小的边的联通性是可以保证的。 
    69     printf("%d",ans);
    70 }
  • 相关阅读:
    java注释
    Java程序的编译与运行
    java 变量-数据类型转换
    java 基本数据类型之四类八种
    编写并解释第一个java程序
    java 基础知识(配置环境变量)
    常用DOS 命令
    java安装文件简介
    mysql权限问题
    vim 文本编辑器
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/8028567.html
Copyright © 2020-2023  润新知