【树】HNOI2014 米特运输
题目大意
洛谷链接
给出一课点带权的树,修改一些点的权值使该树满足:
- 同一个父亲的儿子权值必须相同
- 父亲的取值必须是所有儿子权值之和
输入格式
第一行是一个正整数\(N\),表示节点的数目。
接下来\(N\)行,每行一个正整数,其中的第\(i\)行表示第\(i\)个节点的权值。
再接下来是\(N-1\)行,每行两个正整数\(a,b\)表示\(a,b\)之间有路径(\(a≠b\))。
数据范围
\(N<500000,A[j]<10^8\)
输出格式
输出一个整数表示最少需要修改的点的数目。
样例输入
5
5
4
3
2
1
1 2
1 3
2 4
2 5
样例输出
3
样例解释
一个最优解是将\(A[1]\)改成8,\(A[3]\)改成4,\(A[5]\)改成2。
思路
只要确定了一个点的值就可以知道整棵树的值了。
将路径上权值的累乘即为\(f[i]\),\(f[i]\)相同的表示他们同属于同一种合法方案,最后排序寻找相同最多的即可。
所有权值累乘会超\(long\ long\),这里学到的就是运用\(log\)转为加法,可以不开高精度。
用\(log\)就肯定要开浮点数,本题似乎没有精度问题,不过设个极小值判断一下也是可以的。
\(log(a×b)=log(a)+log(b)\)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=500000+10;
vector<int> edge[maxn];//用vector存下边,比较好写
ll n,cnt=1,ans=1;
double a[maxn],f[maxn];
void add(int x,int y) {
edge[x].push_back(y);
}
void dfs(int rt,double sum) {
f[rt]=sum+log((double)a[rt]);
for(int i=0; i<edge[rt].size(); i++) {
int Next=edge[rt][i];
dfs(Next,sum+log((double)edge[rt].size()));
}
}
int main() {
scanf("%lld",&n);
for(int i=1; i<=n; i++)
scanf("%lf",&a[i]);
for(int i=1; i<n; i++) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
dfs(1,log(1.0));
sort(f+1,f+n+1);
for(int i=2; i<=n; i++)
if(f[i]-f[i-1]<=1e-8) {
cnt++;
ans=max(ans,cnt);
} else cnt=1;
printf("%lld\n",n-ans);
return 0;
}