题目链接: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);
}
}
这篇博客介绍了SPOJ上的MSTS问题,即计算最小生成树的数量。博主探讨了如何利用边的权值和点集的关系来计算不同权值边的最小生成树组合,并提出了通过将相同权值边分组,使用Kruskal算法合并,最终计算生成树数量的方法。建议参考周冬和bjin的相关论文以深入理解。

被折叠的 条评论
为什么被折叠?



