【Kruskal+dfs】BZOJ1016- [JSOI2008]最小生成树计数

【题目大意】

现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。

【思路】

拖欠了三个月整(?)的题目,搞出来弄掉了……本年度写的时候姿势最丑的程序,完全不知道自己在搞些什么,晕乎乎的,算了。

首先,MST具有以下性质:

1.对于同一张无向加权图G,它的最小生成树中长度为L的边长度一定。

2.MST用Kruskal做,处理完长度<=x,此时图的连通性是确定的。

我其实没有读懂第二句话是什么意思,总之大概理解一下,然后乱搞!怎么搞呢。

先按照普通的Kruskal,按照边长排序,然后排序,然后记录下排序为i的长度有几条边numss,下标为nstart到nend。然后弄出一组MST的解。这个不需要单独搞,只需要在记录边的条数的时候一边操作一边进行Kruskal(详细见程序)。

注意一下做完之后有可能进行合并操作的次数,也就是选的边是小于N-1的,也就是没有生成树,特判一下。

接着dfs,枚举每条边取numss个。由于题目条件相同长度的边至多10个,只需暴搜索2^10。每次就判断一下当前这条边左右两边是否已经在同一个并查集里面了,如果不在就可以尝试取这条边合并两段,dfs;或者这条边不取,直接dfs下去。

嗯,然后乘法原理就好了!

什么乱七八糟的题解……

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 #define mod 31011
  7 using namespace std;
  8 const int MAXN=1000+50;
  9 struct node
 10 {
 11     int fr,to,len;
 12     bool operator < (const node &x) const
 13     {
 14         return len<x.len;
 15     }
 16 }edge[MAXN];
 17 int num[MAXN],numss=-1,nstart[MAXN],nend[MAXN];//num[i]长度排序为i的边长需要取多少个,nstart/nend表示长度排序为i的边长排序为几到几 
 18 int n,m,pa[MAXN],tot=0,ans,tmpans; 
 19 int find(int x){return (pa[x]==x?x:find(pa[x]));}
 20 
 21 void dfs(int now,int r,int total,int num)
 22 {
 23     if (num>total) return;
 24     if (now>r)
 25     {
 26         if (num==total) tmpans++;
 27         return;    
 28     }
 29     int u=edge[now].fr,v=edge[now].to;
 30     int fa=find(u),fb=find(v);
 31     if (fa!=fb)
 32     {
 33         pa[fa]=fb;
 34         dfs(now+1,r,total,num+1);
 35         pa[fa]=fa;
 36     } 
 37     dfs(now+1,r,total,num);
 38 }
 39 
 40 void init()
 41 {
 42     scanf("%d%d",&n,&m);
 43     for (int i=0;i<m;i++)
 44     {
 45         int a,b,c;
 46         scanf("%d%d%d",&a,&b,&c);
 47         edge[i]=(node){a,b,c};
 48     }
 49     sort(edge,edge+m);
 50 }
 51 
 52 void kruskal()
 53 {
 54     memset(num,0,sizeof(num));
 55     for (int i=1;i<=n;i++) pa[i]=i;
 56     for (int i=0;i<m;i++)
 57     {
 58         if (i==0 || edge[i].len!=edge[i-1].len)
 59         {
 60             if (i!=1) nend[numss]=i-1;
 61             nstart[++numss]=i;
 62         }
 63         int fa=find(edge[i].fr),fb=find(edge[i].to);
 64         if (fa!=fb)
 65         {
 66             pa[fa]=fb;
 67             num[numss]++;
 68             tot++;
 69         } 
 70     }
 71     nend[numss]=m-1;
 72 }
 73 
 74 void solve()
 75 {
 76     if (tot<n-1) puts("0");
 77     else
 78     {
 79         ans=1;
 80         for (int i=1;i<=n;i++) pa[i]=i;
 81         for (int i=0;i<=numss;i++) 
 82         {
 83             tmpans=0;
 84             dfs(nstart[i],nend[i],num[i],0);
 85             ans=(ans*tmpans)%mod;
 86             for (int j=nstart[i];j<=nend[i];j++)
 87             {
 88                 int u=edge[j].fr,v=edge[j].to;
 89                 int fa=find(u),fb=find(v);
 90                 if (fa!=fb) pa[fa]=fb;
 91             } 
 92         }
 93         printf("%d",ans);
 94     }
 95 }
 96 
 97 int main()
 98 {
 99     freopen("bzoj_1016.in","r",stdin);
100     freopen("bzoj_1016.out","w",stdout);
101     init();
102     kruskal();
103     solve();
104     return 0;    
105 } 

 

posted @ 2016-07-23 21:15  iiyiyi  阅读(231)  评论(0编辑  收藏  举报