bzoj 1016 最小生成树计数
给定一个简单无向有权图,求其最小生成树的个数。
在我们用Kruskal计算最小生成树时,由于相同权值的边选择的顺序是随机的,所以我们最小生成树就也许有很多。
对于同一权值的边,我们不论用什么顺序“扫过”,最终的得到的无向森林的连通性一定是一样的,即对后面的边是否加入的影响也是一样的,所以可以根据这一点将最小生成树分阶段统计,所有权值相同的边为一阶段,每个阶段都有一个方案数,最终的答案便是方案数的乘积。
对于某一阶段的一个选边的合法方案是什么呢?就是这些边加入到图中,使图的连通性和“按任意顺序扫一遍,能加就加”后的连通性一样。
至于怎么计算,先扫一遍,将减少的连通块的数量记下来,然后撤销操作,枚举边集(2^10),判断该边集加入后减少的联通快是否一样,一样就合法。
也可以将到达这一阶段时,图中的联通快缩成一个点,然后计算当前阶段的边和缩了点后的图的联通快,每个连通块计算生成树个数,它们的乘积就是本阶段的方案数
我用的是第二种方法,有点难写
1 #include <cstdio> 2 #include <cstring> 3 #include <map> 4 #include <vector> 5 #include <algorithm> 6 #define abs(a) ((a)<0?-(a):(a)) 7 #define M 31011 8 #define maxn 110 9 using namespace std; 10 11 struct Edge { 12 int u, v, w; 13 Edge( int u, int v, int w ) : u(u), v(v), w(w) {} 14 bool operator<( const Edge & b ) const { 15 return w<b.w; 16 } 17 }; 18 19 typedef int Matrix[maxn][maxn]; 20 21 int n, m; 22 vector<Edge> edge; 23 24 int fa[maxn]; 25 26 void init() { 27 for( int i=1; i<=n; i++ ) fa[i]=i; 28 } 29 int find( int a ) { 30 return fa[a]==a ? a : fa[a]=find(fa[a]); 31 } 32 void unon( int a, int b ) { 33 a = find(a); 34 b = find(b); 35 fa[a] = b; 36 } 37 38 int det( Matrix a, int n ) { 39 for( int i=0; i<n; i++ ) { 40 int r = i; 41 for( int j=i+1; j<n; j++ ) 42 if( abs(a[j][i])>abs(a[r][i]) ) r=j; 43 if( r!=i ) 44 for( int k=i; k<n; k++ ) 45 swap( a[i][k], a[r][k] ); 46 if( a[i][i]==0 ) return 0; 47 for( int j=i+1; j<n; j++ ) { 48 while( a[j][i] ) { 49 int d = a[i][i] / a[j][i]; 50 for( int k=i; k<n; k++ ) { 51 a[i][k] = (a[i][k]-a[j][k]*d%M)%M; 52 swap( a[i][k], a[j][k] ); 53 } 54 } 55 } 56 } 57 int rt = 1; 58 for( int i=0; i<n; i++ ) 59 rt = (rt*a[i][i])%M; 60 return abs(rt); 61 } 62 63 int subfa[30], cnt; 64 Matrix a; 65 int sfind( int a ) { 66 return a==subfa[a] ? a : subfa[a]=sfind(subfa[a]); 67 } 68 void sunon( int a, int b ) { 69 a = sfind(a); 70 b = sfind(b); 71 if( b<a ) subfa[a] = b; 72 else subfa[b] = a; 73 } 74 int pm( map<int,int> &mp, int i ) { return mp[i]; } 75 int calc( const vector<Edge> & e ) { 76 vector<int> cc[30]; 77 map<int,int> mp; 78 cnt = 0; 79 for( int i=0; i<e.size(); i++ ) { 80 int u = e[i].u, v = e[i].v; 81 if( !mp.count(find(u)) ) mp[find(u)]=cnt++; 82 if( !mp.count(find(v)) ) mp[find(v)]=cnt++; 83 } 84 for( int i=0; i<mp.size(); i++ ) subfa[i] = i; 85 for( int i=0; i<e.size(); i++ ) { 86 int u = e[i].u, v = e[i].v; 87 sunon( mp[find(u)], mp[find(v)] ); 88 } 89 int rt = 1; 90 for( int i=0; i<cnt; i++ ) 91 cc[sfind(i)].push_back(i); 92 for( int c=0; cc[c].size(); c++ ) { 93 if( cc[c].size()==1 ) continue; 94 map<int,int> np; 95 for( int t=0; t<cc[c].size(); t++ ) 96 np[cc[c][t]] = t; 97 memset( a, 0, sizeof(a) ); 98 for( int i=0; i<e.size(); i++ ) { 99 int u = np[mp[find(e[i].u)]]; 100 int v = np[mp[find(e[i].v)]]; 101 a[u][u]++; 102 a[v][v]++; 103 a[u][v]--; 104 a[v][u]--; 105 } 106 rt = (rt*det(a,cc[c].size()-1))%M; 107 } 108 return rt; 109 } 110 void work() { 111 sort( edge.begin(), edge.end() ); 112 init(); 113 vector<Edge> e; 114 int ans = 1; 115 for( int i=0; i<edge.size(); ) { 116 do 117 e.push_back( edge[i++] ); 118 while( i<edge.size() && edge[i].w==edge[i-1].w ); 119 ans = (ans*calc(e))%M; 120 while( !e.empty() ) { 121 unon( e.back().u, e.back().v ); 122 e.pop_back(); 123 } 124 } 125 for( int i=2; i<=n; i++ ) 126 if( find(i)!=find(i-1) ) { 127 ans=0; 128 break; 129 } 130 printf( "%d\n", ans ); 131 } 132 133 int main() { 134 scanf( "%d%d", &n, &m ); 135 for( int i=0,u,v,w; i<m; i++ ) { 136 scanf( "%d%d%d", &u, &v, &w ); 137 edge.push_back( Edge(u,v,w) ); 138 } 139 work(); 140 }