【题解】HNOI-2014 米特运输

话说之前一直是一个一个Day地写题解,但由于蒟蒻在短期内大量被J,而且题目也不能全数解决,以后都一题一题地写题解了

Problem

bzoj_gg

题面超长,不建议看题面,像我这种语文得D的废物选手根本看不懂

大意:给定一棵树与每个点的权值,一种合法的点权方案指使得父节点权值为所有儿子节点之和,兄弟节点之间点权相等,并求所有合法点权方案中,与原点权冲突最少的方案(配上样例看看,题意八九不离十)

Solution

这题算是HNOI中比较容易的一道题了

首先明确:最优方案至少保留一个点不便(显然)

模拟下样例发现当一个点权值确定不变时,整棵树都可以推导而得(想一想就知道了)

然后一种基于这种思想的暴力就是枚举哪一个点不变,看看有多少点不变,复杂度 O(n2) O ( n 2 )

然后发现其实很多枚举都是重复的,简化一下:由于一个点确定,整棵树确定,所以只要看看各个点分别确定时根的值即可

考虑快速求解一个点确定时根的取值,发现对于一个节点x(非根)的值不变时,它父亲的值一定是x的权值乘上x父亲的儿子数(因为它的兄弟的权值都相等)

所以x的值确定时根的取值是从根到x路径上所有点的儿子数乘积乘上该点权值

可以从根开始 O(n) O ( n ) 预处理根到x路径上的儿子数乘积,得到n个值乘上该点权值,得到的就是每个点分别确定时根的权值,统计根中相同权值最多的数字即可

对于大量数字相乘,在OI中一般可以采用取对数的方法

xy=xylg(xy)=lgx+lgy x ∗ y = x ∗ y ⇔ lg ⁡ ( x ∗ y ) = lg ⁡ x + lg ⁡ y
(不会的去看高中人教版数学必修1)

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
#define rg register
#define cl(x) memset(x,0,sizeof(x))
#define cl1(x) memset(x,-1,sizeof(x))
#define eps (1e-10)

template <typename _Tp> inline _Tp read(_Tp&x){
    rg char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}

const int N=500500;
struct Edge{int v,nxt;}a[N<<1];
int head[N],tot[N],n,_(0);
double f[N],g[N],d[N];

inline void add(int u,int v){a[++_].v=v,a[_].nxt=head[u],head[u]=_;}

inline void dfs(int x,int fa){
    for(rg int i=head[x];i;i=a[i].nxt)
        if(a[i].v!=fa)
            f[a[i].v]=f[x]+log(tot[x]),
            dfs(a[i].v,x);
    return ;
}

int main(){
    read(n);
    for(rg int i=1;i<=n;++i)read(d[i]);
    cl1(tot);tot[1]=0;
    for(rg int i=1,x,y;i<n;++i)
        read(x),read(y),add(x,y),add(y,x),++tot[x],++tot[y];
    f[1]=log(1);
    dfs(1,0);
    for(rg int i=1;i<=n;++i)
        g[i]=f[i]+log(d[i]);
    sort(g+1,g+n+1);
    int cnt(1),ans(0);
    for(rg int i=2;i<=n;++i)
        if(g[i]-g[i-1]<eps)++cnt;
        else ans=max(ans,cnt),cnt=1;
    ans=max(ans,cnt);
    printf("%d\n",n-ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值