题解 [JSOI2008]最小生成树计数
发现自己啥也不会了回来补一下
- 一张无向图中所有最小生成树中,每种权值的边的个数是相同的
证明:
口胡一种比较好理解的好了
考虑 kruskal 的过程,最小生成树的方案取决于相同权值的边的加入顺序
那么这个过程实际上可以看成每次加入所有权值相同的边,再删去一些这个权值的边使得其中不含简单环
那么这种权值的边数就是完成加边操作后减少的连通块数,与具体的方案是无关的 - 每种权值的边对连通性的影响是独立的,也即一种权值的边的方案在保证连通性不变的前提下可以任意修改
证明过程与上面是相同的
回到本题
那么做法就是求出加入每种权值的边的方案,全乘起来就好了
根据 kruskal 的过程可以得到一种思路:
将最小生成树中除了当前边权以外的边全部加入图中,并查集缩点
只保留原图中这些点间权值为当前权值的边,跑矩阵树
正确性见上面的性质 2
复杂度是 \(O(n^3)\) 的
点击查看~~粘的以前的~~代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
int top, dsu[N], uni[N], id[N], usiz, tot;
ll ans=1;
const ll mod=31011;
struct edge{int from, to, val;}e[N<<1], sta[N];
inline bool operator < (edge a, edge b) {return a.val<b.val;}
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
struct matrix{
int n, m;
ll a[110][110];
void resize(int x, int y) {n=x; m=y; memset(a, 0, sizeof(a));}
inline ll* operator [] (int t) {return a[t];}
ll gauss() {
ll ans=1;
for (int i=1; i<=n; ++i) {
for (int j=i+1; j<=n; ++j) {
while (a[j][i]) {
ll t=a[i][i]/a[j][i];
for (int k=i; k<=m; ++k) a[i][k]=(a[i][k]-a[j][k]*t)%mod;
swap(a[i], a[j]); ans=-ans;
}
}
}
for (int i=1; i<=n; ++i) ans=ans*a[i][i]%mod;
return ans;
}
}mat;
signed main()
{
n=read(); m=read();
for (int i=1; i<=n; ++i) dsu[i]=i;
for (int i=1,u,v,w; i<=m; ++i) {
u=read(); v=read(); w=read();
e[i]={u, v, w};
}
sort(e+1, e+m+1);
for (int i=1,f1,f2; i<=m; ++i) {
f1=find(e[i].from), f2=find(e[i].to);
if (f1==f2) continue;
dsu[f1]=f2;
sta[++top]=e[i];
}
for (int i=1; i<n; ++i) uni[++usiz]=sta[i].val;
sort(uni+1, uni+usiz+1);
usiz=unique(uni+1, uni+usiz+1)-uni-1;
for (int i=1; i<=usiz; ++i) {
for (int j=1; j<=n; ++j) dsu[j]=j, id[j]=0;
for (int j=1; j<n; ++j) if (sta[j].val!=uni[i]) dsu[find(sta[j].from)]=find(sta[j].to);
tot=0;
for (int j=1; j<=n; ++j) if (!id[find(j)]) id[find(j)]=++tot;
mat.resize(tot-1, tot-1);
for (int j=1,s,t; j<=m; ++j) if (e[j].val==uni[i]&&(s=find(e[j].from))!=(t=find(e[j].to))) {
s=id[s]; t=id[t];
++mat[s][s]; ++mat[t][t];
--mat[s][t]; --mat[t][s];
}
ans=ans*mat.gauss()%mod;
}
printf("%lld\n", (ans%mod+mod)%mod);
return 0;
}