題解 P4208 【[JSOI2008]最小生成樹計數】
阿新 • • 發佈:2020-08-21
Solution [JSOI2008]最小生成樹計數
題目大意:給定一張\(n\)個點\(m\)條邊無向圖,保證具有相同邊權的邊不超過\(10\)條,\(n \leq100,m\leq1000\),求不同最小生成樹個數
最小生成樹、計數
分析:
做這題需要用到一個結論:
所有最小生成樹,把邊權按照升序排列之後,得到的序列是一樣的。也就是說對於最小生成樹而言,每種邊權的邊選了多少條是確定的
此外,按照邊權升序的順序加邊,把一種邊權的邊全部加完之後,所有生成樹的連通性是相同的。
由於具有相同邊權的邊的數量較少我們可以暴力
先跑一遍最小生成樹求出每種邊權選擇的數量,然後對每種邊權暴力統計合法方案數,最後用乘法原理計算答案
判斷兩個圖連通性是否相同可以使用並查集,如果\(A\)圖每個點所在的聯通塊編號序列可以通過某種置換得到\(B\)圖對應序列,那麼\(A,B\)兩個圖的連通性就是相同的
#include <cstdio> #include <cctype> #include <cstring> #include <algorithm> using namespace std; const int maxn = 128,maxm = 1024,mod = 31011; inline int read(){ int x = 0;char c = getchar(); while(!isdigit(c))c = getchar(); while(isdigit(c))x = x * 10 + c - '0',c = getchar(); return x; } struct edge{ int u,v,d; bool operator < (const edge &rhs)const{ return d < rhs.d; } }edges[maxm]; int n,m,tot,last,num,mem[16],trans[maxn],ans = 1,cnt; struct mset{ int f[maxn],sz; inline void init(){ for(int i = 1;i <= n;i++)f[i] = i; sz = n; } inline int find(int x){return x == f[x] ? x : f[x] = find(f[x]);} inline void merge(int a,int b){ int x = find(a),y = find(b); f[x] = y;sz--; } }s,tmp,lass; inline void out(){ tmp = lass; for(int i = 1;i <= num;i++){ int x = tmp.find(edges[mem[i]].u),y = tmp.find(edges[mem[i]].v); if(x == y)return; tmp.merge(x,y); } for(int i = 1;i <= n;i++)tmp.f[i] = tmp.find(tmp.f[i]); memset(trans,0,sizeof(trans)); for(int i = 1;i <= n;i++){ if(!trans[lass.f[i]])trans[lass.f[i]] = tmp.f[i]; if(trans[lass.f[i]] != tmp.f[i])return; } cnt++; } inline void dfs(int now,int pos,int mx){ if(pos == num + 1){ out(); return; } if((mx - now + 1) < (num - pos + 1))return; mem[pos] = now; dfs(now + 1,pos + 1,mx); dfs(now + 1,pos,mx); } int main(){ n = read(),m = read(); for(int i = 1;i <= m;i++)edges[i].u = read(),edges[i].v = read(),edges[i].d = read(); sort(edges + 1,edges + 1 + m); s.init(); lass = s; for(int i = 1;i <= m + 1;i++){ if(edges[i].d != edges[i - 1].d){ for(int i = 1;i <= n;i++)s.f[i] = s.find(s.f[i]); cnt = 0; dfs(last,1,i - 1); ans = (ans * cnt) % mod; last = i; lass = s; num = 0; } int x = s.find(edges[i].u),y = s.find(edges[i].v); if(x == y)continue; num++; s.merge(x,y); } printf("%d\n",s.sz == 1 ? ans : 0); return 0; }