时空限制 1000ms / 128MB
题目描述
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。
输入格式:
第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。
接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。
数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
输出格式:
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
说明
说明 1<=n<=100; 1<=m<=1000; 1 ≤ c i ≤ 1 0 9 1\leq c_i\leq 10^9 1≤ci≤109
题目分析
首先最小生成树有这样的性质
若A,B为图G的不同最小生成树
设它们的边从小到大依次为 w a 1 , w a 2 … w a n − 1 w_{a_1},w_{a_2}\dots w_{a_{n-1}} wa1,wa2…wan−1和 w b 1 , w b 2 … w b n − 1 w_{b_1},w_{b_2}\dots w_{b_{n-1}} wb1,wb2…wbn−1
这些边的条数分别为 k a 1 , k a 2 … k a n − 1 k_{a_1},k_{a_2}\dots k_{a_{n-1}} ka1,ka2…kan−1和 k b 1 , k b 2 … k b n − 1 k_{b_1},k_{b_2}\dots k_{b_{n-1}} kb1,kb2…kbn−1
那么有 w a i = w b i w_{a_i}=w_{b_i} wai=wbi且 k a i = k b i k_{a_i}=k_{b_i} kai=kbi
即所有最小生成树的边权出现情况都相同
假设当前已有一个最小生成树,若删去其中所有边权为
w
i
w_i
wi的边,图会变成若干个森林
根据上述性质,此时在原图中所有权值为
w
i
w_i
wi的边中取
k
i
k_i
ki条,若能组成生成树,则一定是最小的
将此时的合法选择方案数记为
c
i
c_i
ci,那么根据乘法原理答案
∏
c
i
\prod c_i
∏ci
所以对于本题,先用Kruskal求出原图的一个最小生成树,记录所有树边和出现过的权值
遍历每个不同权值
w
i
w_i
wi,连接树边中所有权值不为
w
i
w_i
wi的边,将每个联通快缩点
用原图中所有权值为
w
i
w_i
wi的边构造基尔霍夫矩阵,应用矩阵树定理求出
c
i
c_i
ci
乘法原理累乘
c
i
c_i
ci即可
据说这题搜索也能过???
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int mod=31011;
const int maxn=210;
int n,m;
struct node{int u,v,dis;}E[maxn*10],T[maxn];
int fa[maxn],rem[maxn],tot;
int col[maxn],coln;
int a[maxn][maxn];
bool cmp(node a,node b){ return a.dis<b.dis;}
int find(int x)
{
if(x==fa[x]) return x;
else return fa[x]=find(fa[x]);
}
int kruskal()
{
sort(E+1,E+1+m,cmp); int cnt=0;
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i)
{
int u=E[i].u,v=E[i].v;
int fu=find(E[i].u),fv=find(E[i].v);
if(fu!=fv)
{
fa[fu]=fv; T[++cnt]=E[i];
if(E[i].dis!=rem[tot]) rem[++tot]=E[i].dis;
if(cnt==n-1) return 1;
}
}
return 0;
}
void addE(int val)
{
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<n;++i)
if(T[i].dis!=val)
{
int fu=find(T[i].u),fv=find(T[i].v);
if(fu!=fv) fa[fu]=fv;
}
}
void qblock()
{
coln=0;
for(int i=1;i<=n;++i)
if(find(i)==i) col[i]=++coln;
for(int i=1;i<=n;++i)
col[i]=col[find(i)];
}
void build(int val)
{
memset(a,0,sizeof(a));
for(int i=1;i<=m;++i)
if(E[i].dis==val){
int u=col[E[i].u],v=col[E[i].v];
a[u][u]++; a[v][v]++; a[u][v]--; a[v][u]--;
}
}
int gauss(int lim)
{
int res=1;
for(int i=1;i<lim;++i)
{
for(int j=i+1;j<lim;++j)
while(a[j][i])
{
int t=a[i][i]/a[j][i];
for(int k=i;k<lim;++k)
a[i][k]=(a[i][k]-t*a[j][k]+mod)%mod;
swap(a[j],a[i]);
res=-res;
}
res=(res*a[i][i])%mod;
}
return (res+mod)%mod;
}
int main()
{
n=read();m=read();
for(int i=1;i<=m;++i)
E[i].u=read(),E[i].v=read(),E[i].dis=read();
if(!kruskal()){ printf("0"); return 0;}
int ans=1;
for(int i=1;i<=tot;++i)
{
addE(rem[i]); qblock(); build(rem[i]);
ans=ans*gauss(coln)%mod;
}
printf("%d",(ans+mod)%mod);
return 0;
}