zoukankan      html  css  js  c++  java
  • BZOJ_1016_[JSOI2008]_最小生成树计数_(dfs+乘法原理)

    描述


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

    给出一张图,其中具有相同权值的边的数目不超过10,求最小生成树的个数.

    分析


    生成树的计数有一个什么什么算法...

    我真的企图研究了...但是智商捉急的我实在看不懂论文...

    所以最后还是写了暴力...

    当然暴力也要靠正确的姿势的.

    首先来看一个结论:

    同一张图的所有最小生成树中,边权值相同的边的数目是一定的.

    也就是说,假如某一张图的某一棵最小生成树由边权值为1,1,2,2,2,3的边组成,那么这张图的所有其他最小生成树也都由六条权值分别为1,1,2,2,2,3的边组成.

    至于证明,我们可以用数学归纳法.

    1.以上结论对与n=2的情况显然成立.

    2.假设对于n=k的情况以上结论成立即k个节点的最小生成树满足相同权值的边的数目一定.那么对于n=k+1的情况,需要加一条边将第k+1个点连起来,因为生成树是最小的,所以所加的边的权值是一定的,而之前的k-1条边(连接之前的k个点的边)各权值的边的数目也是一定的,所以最终各权值的边的数目是一定的.

    有了这样的一个结论,又因为相同权值的边的数目不超过10,所以我们就可以利用暴力dfs+乘法原理,统计每一种权值的边的选择的方案数,然后相乘即可.

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 
     4 const int maxn=100+5,maxm=1000+5,mod=31011;
     5 struct edge{
     6     int u,v,w;
     7     edge(){}
     8     edge(int u,int v,int w):u(u),v(v),w(w){}
     9     bool operator < (const edge &a) const { return w<a.w; }
    10 }g[maxm];
    11 int n,m,cnt,sum,ans=1;
    12 int a[maxm],b[maxm],ect[maxm],f[maxn];
    13 inline int read(int &x){x=0;int k=1;char c;for(c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')k=-1;for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';return x*=k;}
    14 inline int find(int x){return x==f[x]?x:find(f[x]);}
    15 void dfs(int s,int t,int rem){
    16     if(rem==0){
    17         sum++;
    18         return;
    19     }
    20     for(int i=s;i-1+rem<=t;i++){
    21         int fv=find(g[i].v),fu=find(g[i].u);
    22         if(fu!=fv){
    23             int F=f[fu];
    24             f[fu]=fv;
    25             dfs(i+1,t,rem-1);
    26             f[fu]=F;
    27         }
    28     }
    29 }
    30 inline void solve(){
    31     int i,j;
    32     for(i=1,j=1;i<=m&&j<n;i++){
    33         int fu=find(g[i].u),fv=find(g[i].v);
    34         if(fu!=fv){
    35             f[fu]=fv;
    36             ect[b[i]]++;
    37             j++;
    38         }
    39     }
    40     if(j<n){
    41         puts("0");
    42         return;
    43     }
    44     for(int i=1;i<=n;i++) f[i]=i;
    45     for(i=1;i<=cnt;i++){
    46         sum=0;
    47         dfs(a[i],a[i+1]-1,ect[i]);
    48         for(int j=a[i];j<a[i+1];j++){
    49             int fu=find(g[j].u),fv=find(g[j].v);
    50             if(fu!=fv)     f[fu]=fv;
    51         }
    52         ans=(ans*sum)%mod;
    53     }
    54     printf("%d
    ",ans);
    55 }
    56 inline void init(){
    57     read(n); read(m);
    58     for(int i=1;i<=n;i++) f[i]=i;
    59     for(int i=1,u,v,w;i<=m;i++){
    60         read(u); read(v); read(w);
    61         g[i]=edge(u,v,w);
    62     }
    63     sort(g+1,g+1+m);
    64     for(int i=1;i<=m;i++){
    65         if(g[i].w!=g[i-1].w) a[++cnt]=i;
    66         b[i]=cnt;
    67     }
    68     a[cnt+1]=m+1;
    69 }
    70 int main(){
    71     init();
    72     solve();
    73     return 0;
    74 }
    View Code

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

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

  • 相关阅读:
    awk
    tac
    cat
    less
    more
    head
    vim
    linux安装redis
    Redis for Python开发手册
    Python3.x标准模块库目录
  • 原文地址:https://www.cnblogs.com/Sunnie69/p/5615540.html
Copyright © 2011-2022 走看看