*题解:P3237 [HNOI2014] 米特运输

题目链接

解析

首先观察到一个性质:定一个点的点权,就可以确定整棵树的结点的点权。考虑最终形态,必定有至少一个点的点权没有发生变化(全都变化说明答案为 \(n\),必定更劣)。结合之前得出的性质,我们可以对于每个点,求出这个点点权不变时根节点的权值来记录树的形态,最终看哪个形态出现次数最多即可求出答案。

对于求根节点权值,先想想从下往上传递的过程:每层都是将当前点点权乘上父亲的儿子个数传给父亲。反过来只需要从上往下处理出当前链上每层儿子个数积,再乘上当前点点权就可以求出根节点权值。

还有一个问题,儿子个数乘积可能非常大,所以需要 hash 一下。另外有一种巧妙的方法,利用 \(\log(xy) = \log(x) + \log(y)\) 将求积转化为求和。

最终求答案需要 sort 一下。时间复杂度 \(O(n \log n)\)

代码

注意特判根节点儿子数目。

/*
*/
#include <bits/stdc++.h>
#define eps 0.0000000001
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 5e5 + 5,M = 5e5 + 5,P = 2000000,mod = 998244353;
int a[N];
vector<int> t[N];
vector<double> v;
void dfs(int x,double val,int fa){
	v.push_back(val + log(a[x]));
	val += log(t[x].size() - 1 + (x == 1));
	for(int nx : t[x])if(nx != fa){
		dfs(nx,val,x);
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		t[u].push_back(v);
		t[v].push_back(u);
	}
	dfs(1,log(1),0);
	sort(v.begin(),v.end());
	int res = 2e9;
	int last = 0;
	for(int i=0;i<v.size();i++){
		if(v[i] - v[last] > eps){
			last = i;
		}
		res = min(res,n - (i - last + 1));
	}
	cout<<res;
	return 0;
}
posted @ 2026-06-08 14:35  yutar  阅读(11)  评论(0)    收藏  举报