[bzoj1016] [JSOI2008]最小生成樹計數
阿新 • • 發佈:2019-02-05
() -i %d amp i++ pan bzoj 算法 turn
Description
現在給出了一個簡單無向加權圖。你不滿足於求出這個圖的最小生成樹,而希望知道這個圖中有多少個不同的最小生成樹。(如果兩顆最小生成樹中至少有一條邊不同,則這兩個最小生成樹就是不同的)。由於不同的最小生成樹可能很多,所以你只需要輸出方案數對31011的模就可以了。
Input
第一行包含兩個數,n和m,其中1<=n<=100; 1<=m<=1000; 表示該無向圖的節點數和邊數。每個節點用1~n的整數編號。接下來的m行,每行包含兩個整數:a, b, c,表示節點a, b之間的邊的權值為c,其中1<=c<=1,000,000,000。數據保證不會出現自回邊和重邊。註意:具有相同權值的邊不會超過10條。
Output
輸出不同的最小生成樹有多少個。你只需要輸出數量對31011的模就可以了。
Sample Input
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
Solution
考慮到最小生成樹的算法\(Kruskal\)的過程,顯然可以發現,對於一個圖的不同的最小生成樹,邊權一樣的邊使用的次數也是一樣的。
註意到邊權一樣的邊最多只有\(10\)條,那麽可以對於每種邊權爆搜出一共有多少種情況,然後每種邊權是獨立的,乘起來就好了。
註意特判沒有最小生成樹的情況。
#include<bits/stdc++.h> using namespace std; void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; } #define write(x) printf("%d\n",x) const int maxn = 2e5+10; const int mod = 31011; int n,m,fa[maxn],cnt,tot,ans=1,sum; struct edge{ int u,v,w; bool operator < (const edge &rhs) const {return w<rhs.w;} }e[maxn]; struct data{int l,r,num;}s[maxn]; int find(int x) {return fa[x]==x?x:find(fa[x]);} void dfs(int x,int now,int w) { if(now==s[x].r+1) return sum+=w==s[x].num,void(); dfs(x,now+1,w); int u=find(e[now].u),v=find(e[now].v); if(u!=v) fa[u]=v,dfs(x,now+1,w+1),fa[u]=u,fa[v]=v; } int main() { read(n),read(m); for(int i=1;i<=m;i++) read(e[i].u),read(e[i].v),read(e[i].w); for(int i=1;i<=n;i++) fa[i]=i; sort(e+1,e+m+1); for(int i=1;i<=m;i++) { if(e[i].w!=e[i-1].w) s[++cnt].l=i; if(e[i].w!=e[i+1].w) s[cnt].r=i; int u=find(e[i].u),v=find(e[i].v); if(u!=v) fa[u]=v,s[cnt].num++,tot++; } if(tot!=n-1) return puts("0"),0; for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=cnt;i++) { sum=0;dfs(i,s[i].l,0);ans=1ll*ans*sum%mod; for(int j=s[i].l;j<=s[i].r;j++) { int u=find(e[j].u),v=find(e[j].v); if(u!=v) fa[u]=v; } } write(ans); return 0; }
[bzoj1016] [JSOI2008]最小生成樹計數