題目鏈接
題目描述
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
題解
在所有的最小生成樹中相同權值的邊選的個數都是一樣的,並且互不干擾。
我們可以對每一種相同權值的邊都搜一下有多少種不同的選擇,乘起來就是答案。這題保證了相同權值的邊不超過10,所有搜索是不會超時的。
如果相同權值的邊再多一些,可以用行列式來做。可以參考http://www.cnblogs.com/Fatedayt/archive/2012/05/10/2494877.html
#include<cstdio>
#include<algorithm>
using namespace std;
struct edge{
int x,y,data;
friend bool operator <(edge a,edge b){
return a.data<b.data;
}
}e[1010];
struct node{
int data,num,size;
}c[1010];
int f[110],cnt,tot,n,m,ans,sum;
int get(int x){return f[x]==x?x:get(f[x]);}
bool combine(int x,int y){
x=get(x); y=get(y);
if(x==y) return false;
f[x]=y; return true;
}
void dfs(int x,int y,int k,int now){
if(x>y){
if(k==now) sum++;
return;
}
int p=get(e[x].x),q=get(e[x].y);
if(p!=q){
f[p]=q;
dfs(x+1,y,k,now+1);
f[p]=p;
}
dfs(x+1,y,k,now);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].data);
sort(e+1,e+1+m);
e[m+1].data=-1;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
if(combine(e[i].x,e[i].y)) cnt++,ans++;
if(e[i].data!=e[i+1].data){
c[++tot].data=e[i].data;
c[tot].num=cnt;
c[tot].size=i;
cnt=0;
}
}
ans=ans==n-1;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=tot;i++){
sum=0;
dfs(c[i-1].size+1,c[i].size,c[i].num,0);
ans=(long long)ans*sum%31011;
for(int j=c[i-1].size+1;j<=c[i].size;j++)
combine(e[j].x,e[j].y);
}
printf("%d\n",ans);
return 0;
}