bzoj1016: [JSOI2008]最小生成树计数
最小生成树+dfs。
首先可知某一特定权值的边的数量在不同的最小生成树是确定的。(可以用反证法yy一下)
这样先用kruskal算法求最小生成树,一边统计某种边用的数量。
然后dfs一下(就是枚举每条边有没有,因为相同权值的边最多只有10条,所以是O(2^n)的枚举可以胜任)。
同时要注意图是否联通,不联通输出0.
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 100 + 10; const int maxm = 2000 + 10; const int mod = 31011; struct Edge { int u,v,w; } e[maxm]; int n,m,p,cnt,tot,cur,res=1; struct S { int f[maxn]; int find(int x) { return f[x]==x?x:f[x]=find(f[x]); } bool merge(int u,int v) { int ru=find(u),rv=find(v); if(ru != rv) { f[ru]=rv; return true; } return false; } void init(int n) { for(int i=1;i<=n;i++) f[i]=i; } } s1,s2; bool cmp(Edge a,Edge b) { return a.w < b.w; } void dfs(int l,int r,int cnt,int &cur) { if(l>r) { if(cnt==0) cur++; return; } S tmp = s2; if(s2.merge(e[l].u,e[l].v)) dfs(l+1,r,cnt-1,cur); s2=tmp; dfs(l+1,r,cnt,cur); } void build() { scanf("%d%d",&n,&m); s1.init(n); s2.init(n); for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); sort(e+1,e+m+1,cmp); tot=n,p=1; for(int i=1;i<=m+1;i++) { if(e[i].w != e[i-1].w) { dfs(p,i-1,cnt,cur=0); res=res*cur%mod; p=i; cnt=0; s2=s1; } if(s1.merge(e[i].u,e[i].v)) { cnt++; tot--; } } if(tot==1) printf("%d\n",res); else printf("0\n"); } int main() { build(); return 0; }