专栏文章

题解:P12007 【MX-X10-T3】[LSOT-4] 全国联赛?

P12007题解参与者 2已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@mipqpe5l
此快照首次捕获于
2025/12/03 16:23
3 个月前
此快照最后确认于
2025/12/03 16:23
3 个月前
查看原文

分析题目

首先题目要求把一个森林通过加一些固定的权值的边来得到一棵树,使得树上的点两两的距离之和是最小的。那么为了使得合并之后点两两之间距离和最小,只能去考虑合并两棵带权树的重心。可以发现如果把原来森林里的每一棵树用它的重心表示,则最后的图是一个菊花图。而且为了使得点两两之间距离和最小要把最大的树放在菊花图的中心,次大的用最小的边连接,最小的用最长的边连接,即可。所以换根 DP 求出重心再按照上述策略合并并求出答案。

代码

CPP
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll p = 1e9 + 7;
ll n, m;
vector <pair <ll, ll>> adj[1000005];
ll dp[1000005];
ll siz[1000005];
ll a[1000005];
ll zx, ans;
void dcd(ll u, ll fa)
{
	siz[u] = 1;
	for(auto it:adj[u])
	{
		if(it.first != fa)
		{
			dcd(it.first, u);
			siz[u] += siz[it.first];
			dp[u] += siz[it.first] * it.second + dp[it.first];
		}
	}
}
void dcu(ll u, ll fa)
{
	zx = min(zx, dp[u]);
	for(auto it:adj[u])
	{
		if(it.first != fa)
		{
			ans += siz[it.first] * (siz[u] - siz[it.first]) % p * it.second % p;
			ans %= p;
			dp[it.first] += (dp[u] - dp[it.first] - siz[it.first] * it.second + (siz[u] - siz[it.first]) * it.second);
			siz[it.first] = siz[u];
			dcu(it.first, u);
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);     cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= m; i++)
	{
		ll u, v, w;
		cin >> u >> v >> w;
		adj[u].push_back({v, w});
		adj[v].push_back({u, w});
	}
	vector <ll> v;
	for(int i = 1; i <= n; i++)
	{
		if(!siz[i])
		{
			zx = 0x7f7f7f7f7f7f7f7f;
			dcd(i, 0);
			dcu(i, 0);
			zx %= p;
			ans += zx * (n - siz[i]) % p;
			ans %= p;
			v.push_back(siz[i]);
		}
	}
	sort(v.begin(), v.end());
	for(int i = 1; i <= n - 1 - m; i++)
		cin >> a[i];
	sort(a + 1, a + n - m, greater <ll>());
	for(int i = 1; i <= n - m - 1; i++)
	{
		ans += a[i] * v[i - 1] % p * (n - v[i - 1]) % p;
		ans %= p;
	}
	cout << ans;
	return 0;
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...