BZOJ1016 JSOI2008 最小生成树计数
首先必须知道这样一个性质:同一个图的所有最小生成树等权值的边的数量相等、
那么我们先求任意的一个MST、得到每个权值出现的次数(这里可以先离散化方便处理)、
然后根据题目所给的很好的性质(每个权值出现不超过10次)、对每个权值用2^10枚举取边的情况、然后再看是否还存在一棵MST、
复杂度大概是不到2^10*M*M/10的、、
Code:
#include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <vector> #include <set> #include <map> #include <ctime> #define ps system("pause") #define message printf("*\n") #define pb push_back #define X first #define Y second using namespace std; struct node{ int a,b,w; }edge[2010]; int f[110],use[10010],powe[20]; int cnt,n,m,minnum,ans,cur,res; int find(int x){ if (f[x]==x) return x; f[x]=find(f[x]); return f[x]; } bool cmp(node aa,node bb){ return aa.w<bb.w; } void setup(){ int cur=1; for (int i=1;i<=m;i++) if (edge[i].w!=edge[i+1].w) edge[i].w=cur++; else edge[i].w=cur; } int calc(int x){ int v=0; while (x){ v+=x&1; x/=2; } return v; } bool check(int x,int sit){ cnt=n;res=edge[x].w*use[edge[x].w]; for (int i=1;i<=n;i++) f[i]=i; for (int i=x;edge[i].w==edge[x].w;i++){ if (sit&1){ f[edge[i].a]=find(edge[i].a); f[edge[i].b]=find(edge[i].b); if (f[edge[i].a]!=f[edge[i].b]){ f[f[edge[i].b]]=f[edge[i].a]; --cnt; } } sit/=2; } for (int i=1;i<=m && cnt>1;i++){ if (edge[i].w==edge[x].w) continue; f[edge[i].a]=find(edge[i].a); f[edge[i].b]=find(edge[i].b); if (f[edge[i].a]==f[edge[i].b]) continue; f[f[edge[i].b]]=f[edge[i].a]; res+=edge[i].w; if (--cnt==1) break; } return (cnt==1 && res==minnum); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].w); sort(edge+1,edge+m+1,cmp); setup(); cnt=n;minnum=0; for (int i=1;i<=n;i++) f[i]=i; for (int i=1;i<=m && cnt>1;i++){ f[edge[i].a]=find(edge[i].a); f[edge[i].b]=find(edge[i].b); if (f[edge[i].a]==f[edge[i].b]) continue; f[f[edge[i].b]]=f[edge[i].a]; use[edge[i].w]++; minnum+=edge[i].w; if (--cnt==1) break; } if (cnt!=1){ printf("0\n"); return 0; } ans=1;powe[0]=1; for (int i=1;i<=10;i++) powe[i]=powe[i-1]*2; for (int i=1;i<=m;){ int j=i+1; while (edge[i].w==edge[j].w) j++; if (use[edge[i].w]){ cur=0; for (int sit=0;sit<powe[j-i];sit++) if (calc(sit)==use[edge[i].w] && check(i,sit)) cur++; ans=ans*cur%31011; } i=j; } printf("%d\n",ans); return 0; }