BZOJ 1016: [JSOI2008]最小生成树计数
Solution:
Kruskal+并查集+暴搜
对于每一个符合题意的最小生成树,其每一条树边必定满足:①边权值和一定②对于最小生成树上的每一种权值的边,其数量一定
利用条件②,可以在每一种权值中dfs枚举所有情况,利用并查集判断待取边能否加入(因为回溯需要恢复状态,不能路径压缩)
Code:
1 #include <cstdio> 2 #include <algorithm> 3 #include <iostream> 4 using namespace std; 5 inline int read() 6 { 7 register int f=1,k=0;register char c=getchar(); 8 while (c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); 9 while (c>='0'&&c<='9')k=k*10+c-'0',c=getchar(); 10 return k*f; 11 } 12 const int maxn=1010,MOD=31011; 13 int n,m,cnt,tot,ans=1,sum,fa[maxn]; 14 struct edge{int x,y,v;}e[maxn]; 15 struct data{int l,r,v;}a[maxn]; 16 int find(int now){return fa[now]==now?now:find(fa[now]);} 17 bool cmp(edge a,edge b){return a.v<b.v;} 18 void dfs(int x,int now,int k) 19 { 20 if (now==a[x].r+1) 21 { 22 if (a[x].v==k)sum++; 23 return; 24 } 25 register int q=find(e[now].x),p=find(e[now].y); 26 if (p!=q) 27 { 28 fa[q]=p; 29 dfs(x,now+1,k+1); 30 fa[p]=p;fa[q]=q; 31 } 32 dfs(x,now+1,k); 33 } 34 int main() 35 { 36 n=read();m=read(); 37 for (register int i=1;i<=n;i++)fa[i]=i; 38 for (register int i=1;i<=m;i++) 39 { 40 e[i].x=read();e[i].y=read();e[i].v=read(); 41 } 42 sort(e+1,e+1+m,cmp); 43 for (register int i=1;i<=m;i++) 44 { 45 if (e[i].v!=e[i-1].v)a[++cnt].l=i,a[cnt-1].r=i-1; 46 register int p=find(e[i].x),q=find(e[i].y); 47 if (p!=q) 48 { 49 a[cnt].v++; 50 fa[p]=fa[q]; 51 tot++; 52 } 53 } 54 a[cnt].r=m; 55 if (tot!=n-1) 56 { 57 printf("0\n"); 58 return 0; 59 } 60 for (register int i=1;i<=n;i++)fa[i]=i; 61 for (register int i=1;i<=cnt;i++) 62 { 63 sum=0; 64 dfs(i,a[i].l,0); 65 ans=(ans*sum)%MOD; 66 for (register int j=a[i].l;j<=a[i].r;j++) 67 { 68 register int q=find(e[j].x),p=find(e[j].y); 69 if (p!=q)fa[q]=p; 70 } 71 } 72 printf("%d\n",ans); 73 }