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

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

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 3529  Solved: 1404
    [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,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

    HINT

     

    Source

    题解:同一个图的最小生成树,满足:

    1)同一种权值的边的个数相等

    2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等

    这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法。

    最后根据乘法原理将各种边的选法数乘起来就可以了。

    特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路径压缩。 

    代码们:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<queue>
     6 #include<cstring>
     7 #define PAU putchar(' ')
     8 #define ENT putchar('
    ')
     9 using namespace std;
    10 const int maxn=100+10,maxm=1000+10,inf=-1u>>1,mod=31011;
    11 struct edge{int x,y,w;}e[maxm];struct data{int L,R,num;}a[maxm];
    12 int n,m,cnt,tot,ans=1,sum,fa[maxn];
    13 bool cmp(const edge&a,const edge&b){return a.w<b.w;}
    14 int find(int x){return x==fa[x]?x:find(fa[x]);}
    15 int findset(int x){return x==fa[x]?x:fa[x]=findset(fa[x]);}
    16 void dfs(int x,int pos,int k){
    17     if(pos>a[x].R){if(k==a[x].num)sum++;return;}
    18     int f1=find(e[pos].x),f2=find(e[pos].y);
    19     if(f1!=f2)fa[f1]=f2,dfs(x,-~pos,-~k),fa[f1]=f1;
    20     dfs(x,-~pos,k);return;
    21 }
    22 inline int read(){
    23     int x=0,sig=1;char ch=getchar();
    24     while(!isdigit(ch)){if(ch=='-')sig=-1;ch=getchar();}
    25     while(isdigit(ch))x=10*x+ch-'0',ch=getchar();
    26     return x*=sig;
    27 }
    28 inline void write(int x){
    29     if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x;
    30     int len=0,buf[15];while(x)buf[len++]=x%10,x/=10;
    31     for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return;
    32 }
    33 void init(){
    34     n=read();m=read();
    35     for(int i=1;i<=n;i++) fa[i]=i;
    36     for(int i=1;i<=m;i++) e[i].x=read(),e[i].y=read(),e[i].w=read();
    37     sort(e+1,e+m+1,cmp);
    38     for(int i=1;i<=m;i++){
    39         if(e[i].w!=e[i-1].w){a[++cnt].L=i;a[cnt-1].R=i-1;}
    40         int f1=findset(e[i].x),f2=findset(e[i].y);
    41         if(f1!=f2){fa[f1]=f2;a[cnt].num++;tot++;}
    42     }
    43     return;
    44 }
    45 void work(){
    46     a[cnt].R=m;
    47     if(tot<n-1){write(0);return;}
    48     for(int i=1;i<=n;i++) fa[i]=i;
    49     for(int i=1;i<=cnt;i++){
    50         sum=0;dfs(i,a[i].L,0);
    51         ans=(ans*sum)%mod;
    52         for(int j=a[i].L;j<=a[i].R;j++){
    53             int f1=find(e[j].x),f2=find(e[j].y);
    54             if(f1!=f2)fa[f1]=f2;
    55         }
    56     }write(ans);
    57     return;
    58 }
    59 void print(){
    60     return;
    61 }
    62 int main(){init();work();print();return 0;}
  • 相关阅读:
    jQuery 基础一 样式篇
    javaJavaScript DOM
    linux 实用命令
    Linux下修改.bash_profile 文件改变PATH变量的值
    java 字符串截取的方法
    Telnet命令参考手册
    linux下dubbo调试 ---telnet命令
    【Spring Task】定时任务详解实例-@Scheduled
    Spring定时任务的几种实现
    SQL之case when then用法
  • 原文地址:https://www.cnblogs.com/chxer/p/4648956.html
Copyright © 2011-2022 走看看