BZOJ-3573:[Hnoi2014]米特运输

[文章目录]

Description

给你一个n个节点带有点权的树,要求你修改最少节点的权值,使得每个节点的权值等于其儿子节点权值的和,并且每个节点儿子的权值相同。n<=500000

正难则反,考虑一个节点权值不变,发现如果一个节点的权值固定,整棵树每个节点的权值就固定了。那么我们可以以一号节点的权值作为树区分的标记。对于每个点求出该点权值不变的时候一号节点的权值,最多相同权值个数即为保留的点的个数。
由于每个儿子权值转移到父亲时权值是指数增长的,发现所求出的一号节点的权值巨大。连乘关系???取个对数就好了,变成连加关系。

#include <vector>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
double a[500100],ans[500100],now;
vector<int>ed[500100];
void dfs(int x,int pre)
{
    ans[x]=now+log(a[x]);
    if(x!=1&&ed[x].size()>1) now+=log(ed[x].size()-1);
    else if(x==1&&ed[x].size()) now+=log(ed[x].size());
    for(int i=0;i!=(int)ed[x].size();++i) if(ed[x][i]!=pre)
        dfs(ed[x][i],x);
    if(x!=1&&ed[x].size()>1) now-=log(ed[x].size()-1);
    else if(x==1&&ed[x].size()) now-=log(ed[x].size());
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%lf",a+i);
    int x,y;
    for(int i=1;i!=n;++i)
    {
        scanf("%d%d",&x,&y);
        ed[x].push_back(y); ed[y].push_back(x);
    }
    dfs(1,0); sort(ans+1,ans+n+1);
    int fna=1,cnt=1;
    for(int i=2;i<=n;++i)
    {
        if(fabs(ans[i]-ans[i-1])<1e-5) cnt++;
        else fna=max(fna,cnt),cnt=1;
    }
    fna=max(fna,cnt);
    printf("%d",n-fna);
    return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注