[JSOI2008]最小生成树计数
这个题的思路主要有两种。。。本来博主以为自己的方法仅有自己胡。。。后来又翻到了有的大佬也这么写。。。
代码实现比较简单。。。确定做法正确之后就很快就可以搞定了。。。哈~哈~哈~
最小生成树满足两个性质:
1.它一定是一棵树它一定有 n-1 条边。。。
2.如果是最小的。。。那么优先选择权值小的边。。。
这个地方只要小意会一下就可以了。。。可能有点绕。。。
如果要保证这棵树的权值最小。。。
那么所用的某种权值的边的数量是一定的。。。
所以只要统计一下。。。
题目又很友好的给出了提示具有相同权值的边不会超过10条。。。
辣么我们只要对每种权值的边进行 dfs 判断它的方案数就可以了。。。
呆码:
#include<iostream> #include<cstdio> #include<algorithm> #define mo 31011; using namespace std; int fa[1010],sum,n,m,cnt,tot; struct asd{ int x,y; int dis; } a[1010]; struct sdf{ int l,r,num; } e[1010]; inline int find(int x) { return x==fa[x] ? x : find(fa[x]); } inline void unionn(int x,int y) { fa[x]=y; } inline void unionu(int x,int y) { fa[x]=x; fa[y]=y; } inline bool cmp(asd x,asd y) { return x.dis<y.dis; } inline void dfs(int x,int now,int num) { if(now==e[x].r+1) { if(num==e[x].num) sum++; return; } int xx=find(a[now].x); int yy=find(a[now].y); if(xx!=yy) { unionn(xx,yy); dfs(x,now+1,num+1); unionu(xx,yy); } dfs(x,now+1,num); } int main() { cin>>n>>m; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].dis); sort(a+1,a+1+m,cmp); for(int i=1;i<=m;i++) { if(a[i].dis!=a[i-1].dis) { e[++cnt].l=i; e[cnt-1].r=i-1; } int xx=find(a[i].x); int yy=find(a[i].y); if(xx!=yy) { unionn(xx,yy); e[cnt].num++; tot++; } } e[cnt].r=m; if(tot<n-1) { printf("0"); return 0; } for(int i=1;i<=n;i++) fa[i]=i; int ans=1; for(int i=1;i<=cnt;i++) { sum=0; dfs(i,e[i].l,0); ans=(ans*sum)%mo; for(int j=e[i].l;j<=e[i].r;j++) { int xx=find(a[j].x); int yy=find(a[j].y); if(xx!=yy) unionn(xx,yy); } } printf("%d",ans); }