题目链接:http://www.spoj.com/problems/MSTS/
题目大意:
最小生成树计数
算法:
首先要明白一个性质:
所有最小生成树的权值为ci的边的数量ni和所连接的点集S是一样。
由此可以计算每种权值的边拿出ni个构成S的最小生成树的数量。
那么我们把权值相同的边看成一组,我们先从小到大的求每个组的联通块中生成树的数量。
然后把每个组内的边进行krustal合并,形成一个新图。
我们再对这个图进行生成树计数,最后全部相乘。
关于生成树计数,请参考周冬的论文《生成树的计数及其应用》和bjin的论文《欧几里得算法的应用》。
代码如下:
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> #define mod 31011 using namespace std; vector<pair<int,pair<int,int> > > edge; int M[105][105],p[105]; int n,m; int findr(int x) { if(p[x]<0) return x; return p[x]=findr(p[x]); } long long det(vector<pair<int,int> >& es) { vector<int>hash; for(int i=0; i<es.size(); i++) { hash.push_back(es[i].first); hash.push_back(es[i].second); } sort(hash.begin(),hash.end()); hash.erase(unique(hash.begin(),hash.end()),hash.end()); int n=hash.size(); n--; for(int i=0; i<n; i++) for(int j=0; j<n; j++) M[i][j]=0; for(int i=0; i<es.size(); i++) { int u=lower_bound(hash.begin(),hash.end(),es[i].first)-hash.begin(); int v=lower_bound(hash.begin(),hash.end(),es[i].second)-hash.begin(); M[u][u]++; M[v][v]++; M[u][v]--; M[v][u]--; } long long ans=1LL; for(int i=0; i<n; i++) { for(int j=i+1; j<n; j++) { while(M[j][i]) { ans*=-1; long long t=M[i][i]/M[j][i]; for(int k=i; k<n; k++) { M[i][k]=(M[i][k]-M[j][k]*t)%mod; M[i][k]^=M[j][k]; M[j][k]^=M[i][k]; M[i][k]^=M[j][k]; } } } if(M[i][i]==0) { return 0; } else ans=(ans*M[i][i])%mod; } return (ans%mod+mod)%mod; } int main() { while(~scanf("%d%d",&n,&m)) { edge.clear(); memset(p,-1,sizeof(p)); for(int i=0; i<m; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); u--; v--; edge.push_back(make_pair(w,make_pair(u,v))); } long long ans=1LL; sort(edge.begin(),edge.end()); int l,r=0; while(r<edge.size()) { l=r++; while(r<edge.size()&&edge[r].first==edge[l].first) r++; for(int i=l; i<r; i++) { int& u=edge[i].second.first; int& v=edge[i].second.second; u=findr(u); v=findr(v); } for(int i=l; i<r; i++) { int u=edge[i].second.first; int v=edge[i].second.second; u=findr(u); v=findr(v); if(u!=v) p[u]=v; } for(int i=0; i<n; i++) if(p[i]<0) { vector<pair<int,int> >es; for(int j=l; j<r; j++) { int u=edge[j].second.first; int v=edge[j].second.second; if(findr(u)==i&&findr(v)==i) es.push_back(make_pair(u,v)); } ans=(ans*det(es))%mod; } } int cot=0; for(int i=0; i<n; i++) if(p[i]<0) cot++; if(cot>1) { puts("0"); continue; } printf("%lld\n",(ans%mod+mod)%mod); } }