题意就是让你求有多少种最小生成树
生成树用 kruskal 求就好了
我们考虑用 dfs 中用乘法原理去计数
#include<bits/stdc++.h> #define N 1000100 using namespace std; typedef long long ll; ll n,m,cnt[N],fa[N],ans=1,c[N]; const ll mod=31011; struct edge{ ll u,v,w; }e[N]; inline ll fr(){ register ll s=0,k=1;register char ch=getchar(); while(!isdigit(ch)){if(ch=='-') k=-1;ch=getchar();}; while(isdigit(ch)){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}; return s*k; } inline bool cmp(const edge& x,const edge& y){ return x.w<y.w; } inline ll find(ll x){ return fa[x]==x?x:find(fa[x]);//由于我们合并后还要拆开,所以绝对不能路径压缩 } inline bool kruskal(){//kruskal最小生成树 ll tot=0; for(ll i=1;i<=n;i++) fa[i]=i; for(ll i=1;i<=m;i++){ ll x=find(e[i].u),y=find(e[i].v);//查找 if(x!=y){ tot++; cnt[e[i].w]++; fa[x]=y; } if(tot==n-1) return true; } return false; } inline ll dfs(ll now,ll w,ll s){ if(s==cnt[w]) return 1ll;//如果等于了则算为一种方案 if(e[now].w!=w) return 0ll; ll sum=0,x=find(e[now].u),y=find(e[now].v); if(x!=y){ fa[x]=y;//合并 sum=dfs(now+1,w,s+1);//把这个答案记进来 fa[x]=x,fa[y]=y;//在拆开,方便下面找边时继续合并 } return sum+dfs(now+1,w,s); //算出总的方案数 } int main(){ scanf("%lld%lld",&n,&m); for(ll i=1;i<=m;i++){ scanf("%lld%lld%lld",&e[i].u,&e[i].v,&e[i].w),c[i]=e[i].w; } sort(c+1,c+m+1); sort(e+1,e+m+1,cmp); ll len=unique(c+1,c+m+1)-c-1; for(ll i=1;i<=m;i++){ e[i].w=lower_bound(c+1,c+len+1,e[i].w)-c;//离散化 } if(!kruskal()){ puts("0"); return 0; } for(ll i=1;i<=n;i++) fa[i]=i; for(ll i=1;i<=m;i++){ if(e[i].w==e[i-1].w||!cnt[e[i].w]) continue;//相同的边我们一起处理,如果没有这个权值的边,同样continue ans=ans*dfs(i,e[i].w,0ll)%mod;//计数,运用乘法原理 ll j=i; while(e[j].w==e[i].w){//只要相等,则合并 ll x=find(e[j].u),y=find(e[j].v); fa[x]=y; j++;//每次找新的权值相等的边 } } printf("%lld",ans%mod); return 0; }
标签:JSOI2010,ch,P2143,题解,ll,register,long,isdigit From: https://www.cnblogs.com/dreamau/p/16600011.html