题意
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)数据保证不会出现自回边和重边,具有相同权值的边不会超过10条。
1<=n<=100; 1<=m<=1000;
题解
做这道题需要先推出一些结论。
如果把权值相同的边看做一个边组。则所有最小生成树中,每个边组所用的边都是固定的。而且造成的效果相同。所谓效果相同,前若干组边都选好之后,图的连通性是相同的。这些都比较显然,因为所有最小生成树都是n-1条边,边权和都相同,再考虑kruskal的过程即可得到。
也就是说,每组边之间是相互独立的。可以单独计算方案数然后乘法原理。
由于这题保证具有相同权值的边不会超过10条,直接暴力即可,否则求每组边的方案数要用到基尔霍夫矩阵。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=105, maxe=1005, MOD=31011;
struct Edge{
int x,y,w;
bool operator < (const Edge &b)const{
return w<b.w;
}
} Es[maxe];
struct Group{ int L,R,num; } g[maxe];
int n,m,fa[maxn],G,ans,res;
int getfa(int x){ return fa[x]==x?x:getfa(fa[x]); }
void dfs(int id,int step,int now){
if(step>g[id].R){
if(now==g[id].num) res++;
return;
}
dfs(id,step+1,now);
int fax=getfa(Es[step].x),fay=getfa(Es[step].y);
if(fax!=fay){
fa[fax]=fay;
dfs(id,step+1,now+1);
fa[fax]=fax; fa[fay]=fay;
}
}
int main(){
freopen("bzoj1016.in","r",stdin);
freopen("bzoj1016.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d%d",&Es[i].x,&Es[i].y,&Es[i].w);
sort(Es+1,Es+1+m);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
if(Es[i].w!=Es[i-1].w) g[G].R=i-1, g[++G].L=i;
int fax=getfa(Es[i].x),fay=getfa(Es[i].y);
if(fax!=fay) fa[fax]=fay, g[G].num++, ans++;
} g[G].R=m;
if(ans<n-1){ printf("0\n"); return 0; }
ans=1;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=G;i++){
res=0; dfs(i,g[i].L,0);
ans=ans*res%MOD;
for(int j=g[i].L;j<=g[i].R;j++){
int fax=getfa(Es[j].x),fay=getfa(Es[j].y);
if(fax!=fay) fa[fax]=fay;
}
}
printf("%d\n",ans);
return 0;
}