题目大意
洛谷链接
给出一课点带权的树,修改一些点的权值使该树满足:
- 同一个父亲的儿子权值必须相同
- 父亲的取值必须是所有儿子权值之和
输入格式
第一行是一个正整数(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-ans);
return 0;
}