Solution [JSOI2008]最小生成树计数
题目大意:给定一张(n)个点(m)条边无向图,保证具有相同边权的边不超过(10)条,(n leq100,mleq1000),求不同最小生成树个数
最小生成树、计数
分析:
做这题需要用到一个结论:
所有最小生成树,把边权按照升序排列之后,得到的序列是一样的。也就是说对于最小生成树而言,每种边权的边选了多少条是确定的
此外,按照边权升序的顺序加边,把一种边权的边全部加完之后,所有生成树的连通性是相同的。
由于具有相同边权的边的数量较少我们可以暴力
先跑一遍最小生成树求出每种边权选择的数量,然后对每种边权暴力统计合法方案数,最后用乘法原理计算答案
判断两个图连通性是否相同可以使用并查集,如果(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
",s.sz == 1 ? ans : 0);
return 0;
}