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\n",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 }
1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 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
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1