BZOJ1016 JSOI2008 最小生成树计数 生成树+DFS
题意:求最小生成树的方案数,保证每个边权出现的次数小于十次。
题解:首先我们需要知道:一张图对于一个确定的边权,在任意最小生成树中出现的次数是相同的(请不要问我为什么QAQ)。所以我们先求出每一种边权在MST中出现的次数,然后枚举每一个边权,暴力看取哪些边可以组出一颗MST,复杂度O(M*2^10*M/10)
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; const int Mod=31011; const int MAXN=100+2; const int MAXM=1000+2; struct Edge{ int u,v,w; friend bool operator<(Edge x,Edge y){ return x.w<y.w;} }e[MAXM],t[MAXM]; int N,M,f[MAXN],C=1,Ans,S; int Find(int x){ return x==f[x]?x:Find(f[x]);} void DFS(int x,int l,int w){ if(l==t[x].v+1){ if(w==t[x].w) S++; return; } int a=Find(e[l].u),b=Find(e[l].v); if(a!=b) f[a]=b,DFS(x,l+1,w+1),f[a]=a,f[b]=b; DFS(x,l+1,w); } int main(){ cin >> N >> M; 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); for(int i=1;i<=N;i++) f[i]=i; for(int i=1;i<=M;i++){ if(e[i].w!=e[i-1].w) t[C].v=i-1,t[++C].u=i; int a=Find(e[i].u),b=Find(e[i].v); if(a!=b) f[a]=b,t[C].w++,Ans++; } t[C].v=M; if(Ans<N-1){ cout << 0 << endl; return 0; } Ans=1; for(int i=1;i<=N;i++) f[i]=i; for(int i=1;i<=C;i++){ S=0,DFS(i,t[i].u,0),Ans=Ans*S%Mod; for(int j=t[i].u;j<=t[i].v;j++){ int a=Find(e[j].u),b=Find(e[j].v); if(a!=b) f[a]=b; } } cout << Ans << endl; return 0; }