题解 [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;
}
posted @ 2022-04-01 17:47  Administrator-09  阅读(4)  评论(0编辑  收藏  举报