G. Counting Graphs

发布时间 2023-10-04 11:05:15作者: 不o凡

G. Counting Graphs

题意:添加几条线段,使得图仍保持原先的最小生成树

通过画图我们发现,要添加u->v的线段,线段必须大于u->v的路径内的最大值,不然会破坏原先的最小生成树。
那么该怎么维护路径内的最大值呢?
方法:
1.我们对边的大小进行排序,这样当前边一定大于等于之前的边,只要对当前边进行操作就行。
但是这样可能会造成多个集合,那我们可以利用并查集
2.对于两个集合,它们最多能加的边只有siz_l*siz_r-1(因为不能有重边,所以要减一),那么它们的组合是怎样的?
可以这样想,每条边的选着可以是[S,w),0(不加也是一种选着),所以对于每条边都可以有S-w+1中可能,一共有siz_l * siz_r-1条边,根据乘法原理,它们总的组合是(S-w+1)^(siz_l * siz_r-1)。

3.两集合合并后记得更改其大小

4.那么总的组合就是这些组合的相乘

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e5 + 10,mod= 998244353;
int f[N],siz[N];
int find(int x) {
	return f[x] == x ? x : f[x] = find(f[x]);
}
void solve() {
	LL n, S;
	cin >> n >> S;
	vector<array<LL, 3>> v(n-1);
	for (int i = 0; i < n-1; i++) {
		cin >> v[i][0] >> v[i][1] >> v[i][2];
	}
	//最小生成树
	sort(v.begin(), v.end(), [&](array<LL, 3> &a, array<LL, 3> &b) {
		return a[2] < b[2];
		}
	);

	auto _pow = [](LL a, LL b) {//快速幂
		LL res = 1;
		while (b) {
			if (b & 1) res = res * a % mod;
			a = a * a % mod;
			b >>= 1;
		}
		return res;
		};

	LL ans = 1;

	for (int i = 0; i <= n; i++) f[i] = i,siz[i]=1;//初始化

	for (int i = 0; i < n - 1; i++) {
		int x = find(v[i][0]);
		int y = find(v[i][1]);
		if (x != y) {
			ans = (ans*_pow(S - v[i][2]+1, 1LL*siz[x] * siz[y] - 1))%mod;
			siz[y] += siz[x];//更新集合内的数量
			f[x] = y;//合并到y集合
		}
	}
	cout<< ans;
	cout << "\n";
}
int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}