1. 程式人生 > >BZOJ 1016 最小生成樹計數

BZOJ 1016 最小生成樹計數

題目連結

https://www.lydsy.com/JudgeOnline/problem.php?id=1016

相關部落格

https://blog.csdn.net/sdfzyhx/article/details/52075151

kur產生的最小生成樹只是一組解,但不一定是唯一的解。

(具體分析不寫了)有一個結論,任意最小生成樹中,固定邊權選取的數量是一定的,只是相同的邊權,選取的方式不同,造成的多個解。

   然後對於相同的邊權,子集列舉滿足的方案數數量。最後根據乘法原理求解。

AC程式碼

#include<iostream>
#include<cstdio>
#include
<algorithm> #include<cmath> #include<cstring> using namespace std; const int mxn=10000; int n,m; int sum; int ans=1; // struct edge{// int x,y; int v; }e[mxn]; struct segment{ int st,ed;//區塊起止點 int v; }se[mxn]; int cnt; int cmp(const edge a,const edge b){ return a.v<b.v; }
// int fa[mxn]; int find(int x){ if(fa[x]==x)return x; return find(fa[x]);//不可壓縮 } // void dfs(int x,int now,int t){//x 組編號 now現在處理的邊編號 t使用的邊編號 if(now==se[x].ed+1){ if(t==se[x].v)sum++; return; } int u=find(e[now].x),v=find(e[now].y); if(u!=v){ fa[u]=v; dfs(x,now
+1,t+1);//選用這條邊 fa[u]=u;fa[v]=v;//還原狀態 } dfs(x,now+1,t);//不選這條邊 return; } int main(){ scanf("%d%d",&n,&m); int i,j; for(i=1;i<=n;i++)fa[i]=i;//初始化並查集,處理邊的連通 for(i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v); sort(e+1,e+m+1,cmp); int tot=0;//聯通邊數 for(i=1;i<=m;i++){ if(e[i].v!=e[i-1].v){//如果權值與之前不同 se[cnt].ed=i-1;se[++cnt].st=i;//分到新的一組 } int u=find(e[i].x); int v=find(e[i].y); if(u!=v){fa[u]=v;se[cnt].v++;tot++;} } se[cnt].ed=m; if(tot!=n-1){printf("0");return 0;}//未聯通 for(i=1;i<=n;i++)fa[i]=i;//初始化並查集,處理邊組的連通 for(i=1;i<=cnt;i++){ sum=0; dfs(i,se[i].st,0); ans=(ans*sum)%31011; for(j=se[i].st;j<=se[i].ed;j++){ int u=find(e[j].x),v=find(e[j].y); if(u!=v)fa[u]=v; } } printf("%d",ans%31011); return 0; }