zoukankan      html  css  js  c++  java
  • [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,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
    **定理一:**如果 $A, B$ 同为 $G$ 的最小生成树,且 $A$ 的边权从小到大为 $w(a_1), w(a_2), w(a_3), cdots w(a_n)$,$B$ 的边权从小到大为 $w(b_1), w(b_2), w(b_3), cdots w(b_n)$,则有 $w(a_i) = w(b_i)$。
    **定理二:**如果 $A, B$ 同为 $G$ 的最小生成树,如果 $A, B$ 都从零开始从小到大加边($A$ 加 $A$ 的边,$B$ 加 $B$ 的边)的话,每种权值加完后图的联通性相同。
    假设边权为w的边有很多
    那么每种生成树的w边数是一样的
    根据定理2
    只要算出每种边权下的方案相乘就行了
    算完后给树加边,我们无论怎麽分配w的边,只要尽可能放完就行了
    不会因为分配不同就影响到后面大于w的边的分配
     
    假设s1,s2,s3都是之前加的小边构成的树,他们构成一个森林
    虚线是边权为w,当前要分配的边
    把s1,s2,s3缩成一个点
     
    当前的分配方案就是这个无向图的生成树个数
    可以dfs,也可以矩阵树(dfs做法见此)
    求完后直接往森林中随便加3条边
    但是要考虑这个新图无法建成生成树
    如果是这样的情况,虽然没有把森林联通,但显然也要算方案,但是矩阵树算法会算出0
    所以还要把这个新图加桥,使得正好联通,不会改变矩阵树算出来的方案
    要判断无解的情况
      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 using namespace std;
      7 struct Node
      8 {
      9   int u,v,d;
     10 }edge[2001],edg[2001];
     11 int Mod=31011;
     12 int a[101][101],set[101],tmp[101],set2[101],ans=1,n,m,sum;
     13 bool cmp(Node a,Node b)
     14 {
     15   return a.d<b.d;
     16 }
     17 int gi()
     18 {
     19   char ch=getchar();
     20   int x=0;
     21   while (ch<'0'||ch>'9') ch=getchar();
     22   while (ch>='0'&&ch<='9')
     23     {
     24       x=x*10+ch-'0';
     25       ch=getchar();
     26     }
     27   return x;
     28 }
     29 int find(int x)
     30 {
     31   if (set[x]!=x) set[x]=find(set[x]);
     32   return set[x];
     33 }
     34 int find2(int x)
     35 {
     36   if (set2[x]!=x) set2[x]=find2(set2[x]);
     37   return set2[x];
     38 }
     39 int guass(int S)
     40 {int i,j,k,as=0;
     41   S--;
     42   for (i=1;i<=S;i++)
     43     {
     44       for (j=1;j<=S;j++)
     45     {
     46       a[i][j]=(a[i][j]+Mod)%Mod;
     47     }
     48     }
     49   as=1;
     50   for (i=1;i<=S;i++)
     51     {
     52       for (j=i+1;j<=S;j++)
     53     while (a[j][i])
     54       {
     55         int t=a[i][i]/a[j][i];
     56         for (k=i;k<=S;k++)
     57           {
     58           a[i][k]=(a[i][k]-1ll*t*a[j][k]%Mod+Mod)%Mod;
     59           swap(a[i][k],a[j][k]);
     60           }
     61         as*=-1;
     62       }
     63       as=1ll*as*a[i][i]%Mod;;
     64     }
     65   return (as+Mod)%Mod;
     66 }
     67 void cal(int st,int ed)
     68 {int cnt,i;
     69   cnt=0;
     70   for (i=st;i<=ed;i++)
     71     {
     72       edg[i]=edge[i];
     73       int p=find(edg[i].u),q=find(edg[i].v);
     74       edg[i].u=p;edg[i].v=q;
     75       if (p==q) continue;
     76       tmp[++cnt]=edg[i].u;
     77       tmp[++cnt]=edg[i].v;
     78     }
     79   sort(tmp+1,tmp+cnt+1);
     80   cnt=unique(tmp+1,tmp+cnt+1)-tmp-1;
     81   memset(a,0,sizeof(a));
     82   for (i=1;i<=cnt;i++)
     83     set2[i]=i;
     84   for (i=st;i<=ed;i++)
     85     {
     86       if (edg[i].u==edg[i].v) continue;
     87       int p=find(edg[i].u),q=find(edg[i].v);
     88       if (p!=q)
     89     {
     90       --sum;
     91       set[p]=q;
     92     }
     93       int u=lower_bound(tmp+1,tmp+cnt+1,edg[i].u)-tmp;
     94       int v=lower_bound(tmp+1,tmp+cnt+1,edg[i].v)-tmp;
     95       a[u][u]++;a[v][v]++;
     96       a[u][v]--;a[v][u]--;
     97       p=find2(u),q=find2(v);
     98       if (p!=q) set2[p]=q;
     99     }
    100   for (i=2;i<=cnt;i++)
    101     if (find2(i)!=find2(i-1))
    102       {
    103       int p=find2(i),q=find2(i-1);
    104       a[p][p]++;a[q][q]++;
    105       a[p][q]--;a[q][p]--;
    106       if (p!=q) set2[p]=q;
    107       }
    108   ans=1ll*ans*guass(cnt)%Mod;
    109 }
    110 int main()
    111 {int i;
    112   cin>>n>>m;
    113   for (i=1;i<=m;i++)
    114     {
    115       edge[i].u=gi();edge[i].v=gi();edge[i].d=gi();
    116     }
    117   sort(edge+1,edge+m+1,cmp);
    118   for (i=1;i<=n;i++)
    119     set[i]=i;
    120   sum=n;
    121   int j;
    122   for (i=1;i<=m;i=j)
    123     {
    124       for (j=i;j<=m;j++)
    125       if (edge[j].d!=edge[i].d) break;
    126       if (j-i>1) cal(i,j-1);
    127       else
    128       {
    129         int p=find(edge[i].u),q=find(edge[i].v);
    130         if (p!=q)
    131           {
    132             set[p]=q;
    133             --sum;
    134           }
    135       }
    136     }
    137   if (sum>1) printf("0");
    138   else printf("%d",ans);
    139 }
  • 相关阅读:
    一、Python概念知识点汇总
    在ubuntu永久添加alias
    字符串转化为数字 不用sscanf
    python class类
    《Effective C++》笔记
    问到面向对象该如何回答
    数字和字符串互相转换
    python input print 输入输出
    《c专家编程》笔记
    判断是否是回文数
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/8462255.html
Copyright © 2011-2022 走看看