思路:首先需要了解Matrix_Tree定理
1.设 G是无向图的邻接表,D是无向图各个点的度数
2.令M=D-G,则M的任意n-1阶余子式的行列式值的 绝对值, 就是无向图生成树的个数
应注意的是邻接表的值不只是0,1值,也就是说,当图出现重边的话,邻接表该位置累加。
这道题思路,对于最小生成树,克鲁斯卡尔算法是将边从小到大排序,对于同一大小的边,
在最小生成树中用到的数量是一定的(这个需要好好理解),这样的话,我们每次只需要将满足条件的
同样大小的边统计一下,这样会产生多个联通分量,对每个联通分量 用Matrix_Tree计数。
生成树的点是之前每个联通分量的缩点,固需要 两个并查集。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110;
int n, m, mo, fa[N], ka[N], lin[N][N];
ll ans, M[N][N];
bitset<N> vis;
struct Edge{
int u, v, w;
bool operator <(const Edge &b) const{
return w<b.w;
}
}e[N*10];
void init(){
ans=1;
for(int i=1; i<=n; i++){
fa[i]=ka[i]=i;
}
}
int _find(int f[], int r){
return f[r]==r? r:f[r]=_find(f, f[r]);
}
/*行列式求值模板,将行列式化成上三角行列式,对角线乘积就是答案, 复杂度O(n^3)多一点*/
ll det(int n){
for(int i=0; i<n; i++)
for(int j=0; j<n; j++) M[i][j]%=mo;
ll ret=1;
for(int i=1; i<n; i++){
for(int j=i+1; j<n; j++)
while(M[j][i]){
ll t=M[i][i]/M[j][i];
for(int k=i; k<n; k++) M[i][k]=(M[i][k]-t*M[j][k])%mo;
for(int k=i; k<n; k++) swap(M[i][k], M[j][k]);
ret=-ret;
}
if(M[i][i]==0) return 0ll;
ret=ret*M[i][i]%mo;
}
return (ret+mo)%mo;
}
/***********************************/
void Matrix_Tree(){
vector<int> G[N];
for(int i=1; i<=n; i++)if(vis[i]){//划分连通分量
G[_find(ka, i)].push_back(i);
vis[i]=0;
}
for(int i=1; i<=n; i++)if(G[i].size()){
for(int a=0; a<n; a++) for(int b=0; b<n; b++) M[a][b]=0;
int len=G[i].size();
for(int j=0; j<len; j++){
for(int k=j+1; k<len; k++){
int u=G[i][j], v=G[i][k];
M[j][j]+=lin[u][v];
M[k][k]+=lin[u][v];
M[k][j]-=lin[u][v];
M[j][k]=M[k][j];
}
}
ans=(ans*det(len))%mo;
}
for(int i=1; i<=n; i++)
fa[i]=_find(ka, i);
}
int main(){
while(~scanf("%d%d%d", &n, &m, &mo)&&(n||m||mo)){
for(int i=1; i<=m; i++)
scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
init();
sort(e+1, e+1+m);
int pre=e[1].w;
vis.reset();
memset(lin, 0, sizeof lin);
for(int i=1; i<=m; i++){
int u=e[i].u, v=e[i].v;
int tu=_find(fa, u), tv=_find(fa, v);
if(tu!=tv){
vis[tu]=1; vis[tv]=1;
ka[_find(ka, tu)]=_find(ka, tv);
lin[tu][tv]++; lin[tv][tu]++;
}
if(i==m || pre!=e[i+1].w){
Matrix_Tree();
pre=e[i+1].w;
}
}
bool ok=true;
for(int i=2; i<=n; i++)
if(fa[i]!=fa[i-1]){
ok=false;
break;
}
if(!ok)
printf("0\n");
else
printf("%lld\n", (ans+mo)%mo);
}
return 0;
}
/*
6 6 100
1 2 1
2 3 1
3 4 1
4 1 1
4 5 2
5 6 3
*/