zoukankan      html  css  js  c++  java
  • BZOJ 1016: [JSOI2008]最小生成树计数

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

    题意:

    思路:

    一个无向图所有的最小生成树中某种权值的边的数目均相同。

    引用一篇大牛的证明:

    我们证明以下定理:一个无向图所有的最小生成树中某种权值的边的数目均相同。

    开始时,每个点单独构成一个集合。

    首先只考虑权值最小的边,将它们全部添加进图中,并去掉环,由于是全部尝试添加,那么只要是用这种权值的边能够连通的点,最终就一定能在一个集合中。

    那么不管添加的是哪些边,最终形成的集合数都是一定的,且集合的划分情况一定相同。那么真正添加的边数也是相同的。因为每添加一条边集合的数目便减少1.

    那么权值第二小的边呢?我们将之间得到的集合每个集合都缩为一个点,那么权值第二小的边就变成了当前权值最小的边,也有上述的结论。

    因此每个阶段,添加的边数都是相同的。我们以权值划分阶段,那么也就意味着某种权值的边的数目是完全相同的。

    所以先用克鲁斯卡尔算法计算一遍最小生成树,统计每一权值的边组需要加几条边,最后暴搜,乘法原理相乘即可。注意,要判断无法连通的情况。

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<cstring>
      4 #include<cstdio>
      5 #include<sstream>
      6 #include<vector>
      7 #include<stack>
      8 #include<queue>
      9 #include<cmath>
     10 #include<map>
     11 #include<set>
     12 using namespace std;
     13 typedef long long ll;
     14 typedef pair<int,ll> pll;
     15 const int INF = 0x3f3f3f3f;
     16 const int maxn=10000+5;
     17 const int mod=31011;
     18 
     19 struct node
     20 {
     21     int u,v,w;
     22 }e[10*maxn];
     23 
     24 struct node2
     25 {
     26     int l,r;
     27     int c;
     28 }a[maxn];
     29 
     30 int n, m;
     31 int sum;
     32 int cnt,tot;
     33 int p[maxn];
     34 
     35 bool cmp(node a, node b)
     36 {
     37     return a.w<b.w;
     38 }
     39 
     40 int find(int x)
     41 {
     42     return x==p[x]?x:find(p[x]);
     43 }
     44 
     45 void dfs(int cur, int num ,int pos)
     46 {
     47     if(pos>a[cur].r)
     48     {
     49         if(num==a[cur].c) sum++;
     50         return;
     51     }
     52     int x=find(e[pos].u);
     53     int y=find(e[pos].v);
     54     if(x!=y)
     55     {
     56         p[x]=y;
     57         dfs(cur,num+1,pos+1);
     58         p[x]=x;
     59     }
     60     dfs(cur,num,pos+1);
     61 }
     62 
     63 int main()
     64 {
     65     //freopen("in.txt","r",stdin);
     66     while(~scanf("%d%d",&n,&m))
     67     {
     68         for(int i=1;i<=n;i++) p[i]=i;
     69         for(int i=1;i<=m;i++)
     70             scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
     71 
     72         sort(e+1,e+m+1,cmp);
     73         cnt=0; tot=0;
     74         for(int i=1;i<=m;i++)
     75         {
     76             if(e[i].w!=e[i-1].w)
     77             {
     78                 a[++cnt].l=i;
     79                 a[cnt-1].r=i-1;
     80                 a[cnt].c=0;
     81             }
     82             int x=find(e[i].u);
     83             int y=find(e[i].v);
     84             if(x!=y)
     85             {
     86                 p[x]=y;
     87                 a[cnt].c++;
     88                 tot++;
     89             }
     90         }
     91         a[cnt].r=m;
     92         
     93         if(tot!=n-1)  //连不起来的情况
     94         {
     95             printf("0
    ");
     96             continue;
     97         }
     98         
     99 
    100         for(int i=1;i<=n;i++)   p[i]=i;
    101         int ans=1;
    102         for(int i=1;i<=cnt;i++)
    103         {
    104             sum=0;
    105             dfs(i,0,a[i].l);
    106             ans=(ans*sum)%mod;
    107             for(int j=a[i].l;j<=a[i].r;j++)
    108             {
    109                 int x=find(e[j].u);
    110                 int y=find(e[j].v);
    111                 if(x!=y) p[x]=y;
    112             }
    113         }
    114         printf("%d
    ",ans);
    115     }
    116     return 0;
    117 }
  • 相关阅读:
    【codecombat】 试玩全攻略 第二章 边远地区的森林 一步错
    【codecombat】 试玩全攻略 第十八关 最后的kithman族
    【codecombat】 试玩全攻略 第二章 边远地区的森林 woodlang cubbies
    【codecombat】 试玩全攻略 第二章 边远地区的森林 羊肠小道
    【codecombat】 试玩全攻略 第十七关 混乱的梦境
    【codecombat】 试玩全攻略 第二章 边远地区的森林 林中的死亡回避
    【codecombat】 试玩全攻略 特别关:kithguard斗殴
    【codecombat】 试玩全攻略 第二章 边远地区的森林 森林保卫战
    【codecombat】 试玩全攻略 第二章 边远地区的森林
    实验3 类和对象||
  • 原文地址:https://www.cnblogs.com/zyb993963526/p/7348721.html
Copyright © 2011-2022 走看看