[关闭]
@rebirth1120 2019-11-11T15:04:36.000000Z 字数 1200 阅读 699

[JSOI2008]最小生成树计数 解题报告

解题报告 最小生成树


[JSOI2008]最小生成树计数

题面

有一张节点数为 , 边数为 的带权无向图 ,
求这张图有多少棵不同的最小生成树,
其中相同权值的边不超过 条.

思路

有一个定理/(结论?), 同一张图的不同最小生成树中, 每一种权值的边的数量相等, (并且从小到大把每种权值的边连完后, 图的连通性也一样).

所以权值不同的边之间没有干扰,
那我们只要分别计算每一种权值的边有多少种取的方案.

又因为相同权值的边不超过 条, 所以直接暴搜就行了.

然后还要特判一下图不连通的情况, 输出 .

代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1e2+7;
  4. const int M=1e3+7;
  5. const int mod=31011;
  6. int n,m,fa[N],s=1,t=0,res=0,ans=1;
  7. struct edge{
  8. int x,y,w;
  9. }e[M];
  10. int find(int x){ return x==fa[x] ?x :find(fa[x]); }
  11. void dfs(int k){
  12. if(k>t){
  13. bool flag=1;
  14. for(int i=s;i<=t;i++) if(find(e[i].x)!=find(e[i].y)){ flag=0; break; }
  15. res=(res+flag)%mod;
  16. return;
  17. }
  18. int fx=find(e[k].x),fy=find(e[k].y);
  19. if(fx!=fy){ fa[fx]=fy; dfs(k+1); fa[fx]=fx; }
  20. dfs(k+1);
  21. }
  22. bool rule(edge a,edge b){ return a.w<b.w; }
  23. int main(){
  24. // freopen("x.in","r",stdin);
  25. // freopen("x.out","w",stdout);
  26. cin>>n>>m;
  27. for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
  28. for(int i=1;i<=n;i++) fa[i]=i;
  29. sort(e+1,e+1+m,rule);
  30. for(int i=1;i<=m;i=t+1){
  31. s=i;
  32. do{
  33. t++;
  34. e[t].x=find(e[t].x);
  35. e[t].y=find(e[t].y);
  36. }while(e[t+1].w==e[t].w&&t<=m);
  37. res=0; dfs(s); ans=ans*res%mod;
  38. for(int j=s;j<=t;j++)
  39. if(e[j].x!=e[j].y) fa[find(e[j].x)]=find(e[j].y);
  40. }
  41. int rt=find(1);
  42. for(int i=2;i<=n;i++) if(find(i)!=rt) ans=0;
  43. printf("%d\n",ans);
  44. return 0;
  45. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注