【树】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;
}
posted @ 2020-04-26 17:31  Midoria7  阅读(99)  评论(0编辑  收藏  举报